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*57629Seric static char sccsid [] = "@(#)udb.c 6.2 (Berkeley) 01/20/93 (with USERDB)"; 1251360Seric #else 13*57629Seric static char sccsid [] = "@(#)udb.c 6.2 (Berkeley) 01/20/93 (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 /* 2953654Seric ** UDB.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 */ 3951951Seric 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 5851951Seric /* 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 */ 7251951Seric #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 10251953Seric struct udbent UdbEnts[MAXUDBENT + 1]; 10351953Seric int UdbSock = -1; 10451953Seric bool UdbInitialized = FALSE; 10551360Seric 10651923Seric int 10755012Seric udbexpand(a, sendq, e) 10850581Seric register ADDRESS *a; 10950581Seric ADDRESS **sendq; 11055012Seric register ENVELOPE *e; 11150581Seric { 11250581Seric int i; 11350581Seric register char *p; 11450581Seric DBT key; 11550581Seric DBT info; 11651360Seric bool breakout; 11751360Seric register struct udbent *up; 11851362Seric int keylen; 11957232Seric char keybuf[MAXKEY]; 12057232Seric char buf[BUFSIZ]; 12150581Seric 12250581Seric if (tTd(28, 1)) 12350581Seric printf("expand(%s)\n", a->q_paddr); 12450581Seric 12550581Seric /* make certain we are supposed to send to this address */ 12651909Seric if (bitset(QDONTSEND, a->q_flags)) 12751923Seric return EX_OK; 12855012Seric e->e_to = a->q_paddr; 12950581Seric 13051360Seric /* on first call, locate the database */ 13151951Seric if (!UdbInitialized) 13250581Seric { 13351923Seric extern int _udbx_init(); 13451362Seric 13551923Seric if (_udbx_init() == EX_TEMPFAIL) 13651923Seric return EX_TEMPFAIL; 13751362Seric } 13850581Seric 13951909Seric /* short circuit the process if no chance of a match */ 14051909Seric if (UdbSpec == NULL || UdbSpec[0] == '\0') 14151923Seric return EX_OK; 14251909Seric 14351362Seric /* if name is too long, assume it won't match */ 14451362Seric if (strlen(a->q_user) > sizeof keybuf - 12) 14551923Seric return EX_OK; 14651360Seric 14751362Seric /* if name begins with a colon, it indicates our metadata */ 14851362Seric if (a->q_user[0] == ':') 14951923Seric return EX_OK; 15051360Seric 15151362Seric /* build actual database key */ 15251362Seric (void) strcpy(keybuf, a->q_user); 15351362Seric (void) strcat(keybuf, ":maildrop"); 15451362Seric keylen = strlen(keybuf); 15551360Seric 15651360Seric breakout = FALSE; 15751362Seric for (up = UdbEnts; !breakout; up++) 15850581Seric { 15951360Seric char *user; 16051360Seric struct timeval timeout; 16151360Seric fd_set fdset; 16250581Seric 16351360Seric /* 16451360Seric ** Select action based on entry type. 16551360Seric ** 16651360Seric ** On dropping out of this switch, "class" should 16751360Seric ** explain the type of the data, and "user" should 16851360Seric ** contain the user information. 16951360Seric */ 17050581Seric 17151360Seric switch (up->udb_type) 17251360Seric { 17351951Seric case UDB_DBFETCH: 17451362Seric key.data = keybuf; 17551362Seric key.size = keylen; 17651362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_CURSOR); 17751923Seric if (i > 0 || info.size <= 0) 17851360Seric { 17951360Seric if (tTd(28, 2)) 18051362Seric printf("expand: no match on %s\n", keybuf); 18151360Seric continue; 18251360Seric } 18350581Seric 18451830Seric while (i == 0 && key.size == keylen && 18551830Seric bcmp(key.data, keybuf, keylen) == 0) 18651362Seric { 18751830Seric breakout = TRUE; 18851362Seric if (info.size < sizeof buf) 18951362Seric user = buf; 19051362Seric else 19151362Seric user = xalloc(info.size + 1); 19251362Seric bcopy(info.data, user, info.size); 19351362Seric user[info.size] = '\0'; 19450581Seric 19551362Seric message(Arpa_Info, "expanded to %s", user); 19651362Seric AliasLevel++; 19755012Seric sendtolist(user, a, sendq, e); 19851362Seric AliasLevel--; 19951362Seric 20051362Seric if (user != buf) 20151362Seric free(user); 20251362Seric 20351362Seric /* get the next record */ 20451362Seric i = (*up->udb_dbp->seq)(up->udb_dbp, &key, &info, R_NEXT); 20551830Seric } 20651923Seric if (i < 0) 20751923Seric { 20851923Seric syserr("udbexpand: db-get stat %s"); 20951923Seric return EX_TEMPFAIL; 21051923Seric } 21151360Seric break; 21251360Seric 21351360Seric case UDB_REMOTE: 21451741Seric /* not yet implemented */ 21551741Seric continue; 21651362Seric 21751360Seric case UDB_FORWARD: 21851360Seric i = strlen(up->udb_fwdhost) + strlen(a->q_user) + 1; 21951360Seric if (i < sizeof buf) 22051360Seric user = buf; 22151360Seric else 22251360Seric user = xalloc(i + 1); 22351360Seric (void) sprintf(user, "%s@%s", a->q_user, up->udb_fwdhost); 22451362Seric message(Arpa_Info, "expanded to %s", user); 22551362Seric AliasLevel++; 22655012Seric sendtolist(user, a, sendq, e); 22751362Seric AliasLevel--; 22851362Seric if (user != buf) 22951362Seric free(user); 23051362Seric breakout = TRUE; 23151360Seric break; 23251360Seric 23351360Seric case UDB_EOLIST: 23451360Seric breakout = TRUE; 23551360Seric continue; 23651360Seric 23751360Seric default: 23851360Seric /* unknown entry type */ 23951360Seric continue; 24051360Seric } 24151362Seric } 24251923Seric return EX_OK; 24351362Seric } 24451951Seric /* 24551951Seric ** UDBSENDER -- return canonical external name of sender, given local name 24651951Seric ** 24751951Seric ** Parameters: 24851951Seric ** sender -- the name of the sender on the local machine. 24951951Seric ** 25051951Seric ** Returns: 25151951Seric ** The external name for this sender, if derivable from the 25251951Seric ** database. 25351951Seric ** NULL -- if nothing is changed from the database. 25451951Seric ** 25551951Seric ** Side Effects: 25651951Seric ** none. 25751951Seric */ 25851360Seric 25951951Seric char * 26051951Seric udbsender(sender) 26151951Seric char *sender; 26251951Seric { 26351951Seric register char *p; 26451951Seric register struct udbent *up; 26551951Seric int i; 26651951Seric int keylen; 26751951Seric DBT key, info; 26857232Seric char keybuf[MAXKEY]; 26951951Seric 27051951Seric if (tTd(28, 1)) 27151951Seric printf("udbsender(%s)\n", sender); 27251951Seric 27351951Seric if (!UdbInitialized) 27451951Seric { 27551951Seric if (_udbx_init() == EX_TEMPFAIL) 27651951Seric return NULL; 27751951Seric } 27851951Seric 27951951Seric /* short circuit if no spec */ 28051951Seric if (UdbSpec == NULL || UdbSpec[0] == '\0') 28151951Seric return NULL; 28251951Seric 28351951Seric /* long names can never match and are a pain to deal with */ 28451951Seric if (strlen(sender) > sizeof keybuf - 12) 28551951Seric return NULL; 28651951Seric 28751951Seric /* names beginning with colons indicate metadata */ 28851951Seric if (sender[0] == ':') 28951951Seric return NULL; 29051951Seric 29151951Seric /* build database key */ 29251951Seric (void) strcpy(keybuf, sender); 29351951Seric (void) strcat(keybuf, ":mailname"); 29451951Seric keylen = strlen(keybuf); 29551951Seric 29651951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 29751951Seric { 29851951Seric /* 29951951Seric ** Select action based on entry type. 30051951Seric */ 30151951Seric 30251951Seric switch (up->udb_type) 30351951Seric { 30451951Seric case UDB_DBFETCH: 30551951Seric key.data = keybuf; 30651951Seric key.size = keylen; 30751951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 30851951Seric if (i != 0 || info.size <= 0) 30951951Seric { 31051951Seric if (tTd(28, 2)) 31151951Seric printf("udbsender: no match on %s\n", 31251951Seric keybuf); 31351951Seric continue; 31451951Seric } 31551951Seric 31651951Seric p = xalloc(info.size + 1); 31751951Seric bcopy(info.data, p, info.size); 31851951Seric p[info.size] = '\0'; 31951951Seric if (tTd(28, 1)) 32051951Seric printf("udbsender ==> %s\n", p); 32151951Seric return p; 32251951Seric } 32351951Seric } 32451951Seric 32551951Seric /* 32651951Seric ** Nothing yet. Search again for a default case. But only 32751951Seric ** use it if we also have a forward (:maildrop) pointer already 32851951Seric ** in the database. 32951951Seric */ 33051951Seric 33151951Seric /* build database key */ 33251951Seric (void) strcpy(keybuf, sender); 33351951Seric (void) strcat(keybuf, ":maildrop"); 33451951Seric keylen = strlen(keybuf); 33551951Seric 33651951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 33751951Seric { 33851951Seric switch (up->udb_type) 33951951Seric { 34051951Seric case UDB_DBFETCH: 34151951Seric /* get the default case for this database */ 34251951Seric if (up->udb_default == NULL) 34351951Seric { 34451951Seric key.data = ":default:mailname"; 34551951Seric key.size = strlen(key.data); 34651951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 34751951Seric if (i != 0 || info.size <= 0) 34851951Seric { 34951951Seric /* no default case */ 35051951Seric up->udb_default = ""; 35151951Seric continue; 35251951Seric } 35351951Seric 35451951Seric /* save the default case */ 35551951Seric up->udb_default = xalloc(info.size + 1); 35651951Seric bcopy(info.data, up->udb_default, info.size); 35751951Seric up->udb_default[info.size] = '\0'; 35851951Seric } 35951951Seric else if (up->udb_default[0] == '\0') 36051951Seric continue; 36151951Seric 36251951Seric /* we have a default case -- verify user:maildrop */ 36351951Seric key.data = keybuf; 36451951Seric key.size = keylen; 36551951Seric i = (*up->udb_dbp->get)(up->udb_dbp, &key, &info, 0); 36651951Seric if (i != 0 || info.size <= 0) 36751951Seric { 36851951Seric /* nope -- no aliasing for this user */ 36951951Seric continue; 37051951Seric } 37151951Seric 37251951Seric /* they exist -- build the actual address */ 37351951Seric p = xalloc(strlen(sender) + strlen(up->udb_default) + 2); 37451951Seric (void) strcpy(p, sender); 37551951Seric (void) strcat(p, "@"); 37651951Seric (void) strcat(p, up->udb_default); 37751951Seric if (tTd(28, 1)) 37851951Seric printf("udbsender ==> %s\n", p); 37951951Seric return p; 38051951Seric } 38151951Seric } 38251951Seric 38351951Seric /* still nothing.... too bad */ 38451951Seric return NULL; 38551951Seric } 38651951Seric /* 38751951Seric ** _UDBX_INIT -- parse the UDB specification, opening any valid entries. 38851951Seric ** 38951951Seric ** Parameters: 39051951Seric ** none. 39151951Seric ** 39251951Seric ** Returns: 39351951Seric ** EX_TEMPFAIL -- if it appeared it couldn't get hold of a 39451951Seric ** database due to a host being down or some similar 39551951Seric ** (recoverable) situation. 39651951Seric ** EX_OK -- otherwise. 39751951Seric ** 39851951Seric ** Side Effects: 39951951Seric ** Fills in the UdbEnts structure from UdbSpec. 40051951Seric */ 40151951Seric 40251363Seric #define MAXUDBOPTS 27 40351363Seric 40451953Seric int 40551362Seric _udbx_init() 40651362Seric { 40751362Seric register char *p; 40851362Seric int i; 40951362Seric register struct udbent *up; 41057232Seric char buf[BUFSIZ]; 41151360Seric 41251951Seric if (UdbInitialized) 41351951Seric return EX_OK; 41451951Seric 41551908Seric # ifdef UDB_DEFAULT_SPEC 41651908Seric if (UdbSpec == NULL) 41751908Seric UdbSpec = UDB_DEFAULT_SPEC; 41851908Seric # endif 41951908Seric 42051362Seric p = UdbSpec; 42151362Seric up = UdbEnts; 42251762Seric while (p != NULL) 42351362Seric { 42451362Seric char *spec; 42551362Seric auto int rcode; 42651363Seric int nopts; 42751362Seric int nmx; 42851362Seric register struct hostent *h; 42951362Seric char *mxhosts[MAXMXHOSTS + 1]; 43051363Seric struct option opts[MAXUDBOPTS + 1]; 43151362Seric 43251362Seric while (*p == ' ' || *p == '\t' || *p == ',') 43351362Seric p++; 43451362Seric if (*p == '\0') 43551362Seric break; 43651362Seric spec = p; 43756795Seric p = strchr(p, ','); 43851761Seric if (p != NULL) 43951362Seric *p++ = '\0'; 44051363Seric 44151363Seric /* extract options */ 44251363Seric nopts = _udb_parsespec(spec, opts, MAXUDBOPTS); 44351363Seric 44451363Seric /* 44551363Seric ** Decode database specification. 44651363Seric ** 44751363Seric ** In the sendmail tradition, the leading character 44851363Seric ** defines the semantics of the rest of the entry. 44951363Seric ** 45051363Seric ** +hostname -- send a datagram to the udb server 45151363Seric ** on host "hostname" asking for the 45251363Seric ** home mail server for this user. 45351363Seric ** *hostname -- similar to +hostname, except that the 45451363Seric ** hostname is searched as an MX record; 45551363Seric ** resulting hosts are searched as for 45651363Seric ** +mxhostname. If no MX host is found, 45751363Seric ** this is the same as +hostname. 45851363Seric ** @hostname -- forward email to the indicated host. 45951363Seric ** This should be the last in the list, 46051363Seric ** since it always matches the input. 46151363Seric ** /dbname -- search the named database on the local 46251363Seric ** host using the Berkeley db package. 46351363Seric */ 46451363Seric 46551362Seric switch (*spec) 46651360Seric { 46751362Seric case '+': /* search remote database */ 46851363Seric case '*': /* search remote database (expand MX) */ 46951363Seric if (*spec == '*') 47051363Seric { 471*57629Seric #ifdef NAMED_BIND 47251363Seric nmx = getmxrr(spec + 1, mxhosts, "", &rcode); 473*57629Seric #else 474*57629Seric mxhosts[0] = spec + 1; 475*57629Seric nmx = 1; 476*57629Seric rcode = 0; 477*57629Seric #endif 47851363Seric if (tTd(28, 16)) 47951363Seric { 48051363Seric int i; 48151362Seric 48251363Seric printf("getmxrr(%s): %d", spec + 1, nmx); 48351363Seric for (i = 0; i <= nmx; i++) 48451363Seric printf(" %s", mxhosts[i]); 48551363Seric printf("\n"); 48651363Seric } 48751363Seric } 48851363Seric else 48951362Seric { 49051363Seric nmx = 1; 49151363Seric mxhosts[0] = spec + 1; 49251362Seric } 49351362Seric 49451362Seric for (i = 0; i < nmx; i++) 49551362Seric { 49651362Seric h = gethostbyname(mxhosts[i]); 49751362Seric if (h == NULL) 49851362Seric continue; 49951362Seric up->udb_type = UDB_REMOTE; 50051362Seric up->udb_addr.sin_family = h->h_addrtype; 50151362Seric bcopy(h->h_addr_list[0], 50251362Seric (char *) &up->udb_addr.sin_addr, 50351362Seric h->h_length); 50451362Seric up->udb_addr.sin_port = UdbPort; 50551362Seric up->udb_timeout = UdbTimeout; 50651362Seric up++; 50751362Seric } 50851362Seric 50951362Seric /* set up a datagram socket */ 51051362Seric if (UdbSock < 0) 51151362Seric { 51251362Seric UdbSock = socket(AF_INET, SOCK_DGRAM, 0); 51351362Seric (void) fcntl(UdbSock, F_SETFD, 1); 51451362Seric } 51551362Seric break; 51651362Seric 51751362Seric case '@': /* forward to remote host */ 51851362Seric up->udb_type = UDB_FORWARD; 51951362Seric up->udb_fwdhost = spec + 1; 52051362Seric up++; 52151362Seric break; 52251362Seric 52351362Seric case '/': /* look up remote name */ 52451831Seric up->udb_dbname = spec; 52551923Seric errno = 0; 52651362Seric up->udb_dbp = dbopen(spec, O_RDONLY, 0644, DB_BTREE, NULL); 52751362Seric if (up->udb_dbp == NULL) 52851923Seric { 52951923Seric if (errno != ENOENT && errno != EACCES) 53051951Seric { 53151951Seric up->udb_type = UDB_EOLIST; 53251951Seric goto tempfail; 53351951Seric } 53451362Seric break; 53551923Seric } 53651951Seric up->udb_type = UDB_DBFETCH; 53751362Seric up++; 53851362Seric break; 53951360Seric } 54051362Seric } 54151362Seric up->udb_type = UDB_EOLIST; 54251360Seric 54351362Seric if (tTd(28, 4)) 54451362Seric { 54551951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 54651360Seric { 54751362Seric switch (up->udb_type) 54851362Seric { 54951362Seric case UDB_REMOTE: 55051362Seric printf("REMOTE: addr %s, timeo %d\n", 55151362Seric inet_ntoa(up->udb_addr.sin_addr), 55251362Seric up->udb_timeout); 55351362Seric break; 55451362Seric 55551951Seric case UDB_DBFETCH: 55651951Seric printf("FETCH: file %s\n", 55751830Seric up->udb_dbname); 55851362Seric break; 55951362Seric 56051362Seric case UDB_FORWARD: 56151362Seric printf("FORWARD: host %s\n", 56251362Seric up->udb_fwdhost); 56351362Seric break; 56451362Seric 56551362Seric default: 56651362Seric printf("UNKNOWN\n"); 56751362Seric break; 56851362Seric } 56951360Seric } 57050581Seric } 57151951Seric 57251951Seric UdbInitialized = TRUE; 57351955Seric errno = 0; 57451951Seric return EX_OK; 57551951Seric 57651951Seric /* 57751951Seric ** On temporary failure, back out anything we've already done 57851951Seric */ 57951951Seric 58051951Seric tempfail: 58151951Seric for (up = UdbEnts; up->udb_type != UDB_EOLIST; up++) 58251951Seric { 58351951Seric if (up->udb_type == UDB_DBFETCH) 58451951Seric { 58551951Seric (*up->udb_dbp->close)(up->udb_dbp); 58651951Seric } 58751951Seric } 58851951Seric return EX_TEMPFAIL; 58951360Seric } 59050581Seric 59151363Seric int 59251363Seric _udb_parsespec(udbspec, opt, maxopts) 59351363Seric char *udbspec; 59451363Seric struct option opt[]; 59551363Seric int maxopts; 59651363Seric { 59751363Seric register char *spec; 59851363Seric register char *spec_end; 59951363Seric register int optnum; 60051363Seric 60156795Seric spec_end = strchr(udbspec, ':'); 60251363Seric for (optnum = 0; optnum < maxopts && (spec = spec_end) != NULL; optnum++) 60351363Seric { 60451363Seric register char *p; 60551363Seric 60651363Seric while (isspace(*spec)) 60751363Seric spec++; 60856795Seric spec_end = strchr(spec, ':'); 60951363Seric if (spec_end != NULL) 61051363Seric *spec_end++ = '\0'; 61151363Seric 61251363Seric opt[optnum].name = spec; 61351363Seric opt[optnum].val = NULL; 61456795Seric p = strchr(spec, '='); 61551363Seric if (p != NULL) 61651363Seric opt[optnum].val = ++p; 61751363Seric } 61851363Seric return optnum; 61951363Seric } 62051363Seric 62151360Seric #else /* not USERDB */ 62251360Seric 62351923Seric int 62455012Seric udbexpand(a, sendq, e) 62551360Seric ADDRESS *a; 62651360Seric ADDRESS **sendq; 62755012Seric ENVELOPE *e; 62851360Seric { 62951923Seric return EX_OK; 63050581Seric } 63150581Seric 63250581Seric #endif /* USERDB */ 633