138032Speter/* 2261363Sgshapiro * Copyright (c) 1998-2003 Proofpoint, Inc. and its suppliers. 364562Sgshapiro * All rights reserved. 438032Speter * Copyright (c) 1983, 1995-1997 Eric P. Allman. All rights reserved. 564562Sgshapiro * Copyright (c) 1988, 1993 664562Sgshapiro * The Regents of the University of California. All rights reserved. 738032Speter * 838032Speter * By using this file, you agree to the terms and conditions set 938032Speter * forth in the LICENSE file which can be found at the top level of 1038032Speter * the sendmail distribution. 1190792Sgshapiro * 1238032Speter */ 1338032Speter 1464562Sgshapiro#include <sendmail.h> 1538032Speter 16266692SgshapiroSM_RCSID("@(#)$Id: alias.c,v 8.221 2013-11-22 20:51:54 ca Exp $") 1738032Speter 1890792Sgshapiro#define SEPARATOR ':' 1964562Sgshapiro# define ALIAS_SPEC_SEPARATORS " ,/:" 2038032Speter 2164562Sgshapirostatic MAP *AliasFileMap = NULL; /* the actual aliases.files map */ 2264562Sgshapirostatic int NAliasFileMaps; /* the number of entries in AliasFileMap */ 2364562Sgshapiro 2490792Sgshapirostatic char *aliaslookup __P((char *, int *, char *)); 2564562Sgshapiro 2690792Sgshapiro/* 2738032Speter** ALIAS -- Compute aliases. 2838032Speter** 2938032Speter** Scans the alias file for an alias for the given address. 3038032Speter** If found, it arranges to deliver to the alias list instead. 3138032Speter** Uses libdbm database if -DDBM. 3238032Speter** 3338032Speter** Parameters: 3438032Speter** a -- address to alias. 3538032Speter** sendq -- a pointer to the head of the send queue 3638032Speter** to put the aliases in. 3738032Speter** aliaslevel -- the current alias nesting depth. 3838032Speter** e -- the current envelope. 3938032Speter** 4038032Speter** Returns: 4138032Speter** none 4238032Speter** 4338032Speter** Side Effects: 4438032Speter** Aliases found are expanded. 4538032Speter** 4638032Speter** Deficiencies: 4738032Speter** It should complain about names that are aliased to 4838032Speter** nothing. 4938032Speter*/ 5038032Speter 5138032Spetervoid 5238032Speteralias(a, sendq, aliaslevel, e) 5338032Speter register ADDRESS *a; 5438032Speter ADDRESS **sendq; 5538032Speter int aliaslevel; 5638032Speter register ENVELOPE *e; 5738032Speter{ 5838032Speter register char *p; 5938032Speter char *owner; 6064562Sgshapiro auto int status = EX_OK; 6138032Speter char obuf[MAXNAME + 7]; 6238032Speter 6338032Speter if (tTd(27, 1)) 6490792Sgshapiro sm_dprintf("alias(%s)\n", a->q_user); 6538032Speter 6638032Speter /* don't realias already aliased names */ 6764562Sgshapiro if (!QS_IS_OK(a->q_state)) 6838032Speter return; 6938032Speter 7038032Speter if (NoAlias) 7138032Speter return; 7238032Speter 7338032Speter e->e_to = a->q_paddr; 7438032Speter 7538032Speter /* 7638032Speter ** Look up this name. 7738032Speter ** 7838032Speter ** If the map was unavailable, we will queue this message 7938032Speter ** until the map becomes available; otherwise, we could 8038032Speter ** bounce messages inappropriately. 8138032Speter */ 8238032Speter 8364562Sgshapiro#if _FFR_REDIRECTEMPTY 8464562Sgshapiro /* 8564562Sgshapiro ** envelope <> can't be sent to mailing lists, only owner- 8664562Sgshapiro ** send spam of this type to owner- of the list 8764562Sgshapiro ** ---- to stop spam from going to mailing lists! 8864562Sgshapiro */ 8990792Sgshapiro 9064562Sgshapiro if (e->e_sender != NULL && *e->e_sender == '\0') 9138032Speter { 9264562Sgshapiro /* Look for owner of alias */ 93168515Sgshapiro (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user); 9490792Sgshapiro if (aliaslookup(obuf, &status, a->q_host) != NULL) 9564562Sgshapiro { 9664562Sgshapiro if (LogLevel > 8) 97120256Sgshapiro sm_syslog(LOG_WARNING, e->e_id, 9864562Sgshapiro "possible spam from <> to list: %s, redirected to %s\n", 9964562Sgshapiro a->q_user, obuf); 10090792Sgshapiro a->q_user = sm_rpool_strdup_x(e->e_rpool, obuf); 10164562Sgshapiro } 10264562Sgshapiro } 10364562Sgshapiro#endif /* _FFR_REDIRECTEMPTY */ 10464562Sgshapiro 10590792Sgshapiro p = aliaslookup(a->q_user, &status, a->q_host); 10664562Sgshapiro if (status == EX_TEMPFAIL || status == EX_UNAVAILABLE) 10764562Sgshapiro { 10864562Sgshapiro a->q_state = QS_QUEUEUP; 10938032Speter if (e->e_message == NULL) 110168515Sgshapiro e->e_message = sm_rpool_strdup_x(e->e_rpool, 111168515Sgshapiro "alias database unavailable"); 11290792Sgshapiro 11390792Sgshapiro /* XXX msg only per recipient? */ 11490792Sgshapiro if (a->q_message == NULL) 11590792Sgshapiro a->q_message = "alias database unavailable"; 11638032Speter return; 11738032Speter } 11838032Speter if (p == NULL) 11938032Speter return; 12038032Speter 12138032Speter /* 12238032Speter ** Match on Alias. 12338032Speter ** Deliver to the target list. 12438032Speter */ 12538032Speter 12638032Speter if (tTd(27, 1)) 12790792Sgshapiro sm_dprintf("%s (%s, %s) aliased to %s\n", 12890792Sgshapiro a->q_paddr, a->q_host, a->q_user, p); 12938032Speter if (bitset(EF_VRFYONLY, e->e_flags)) 13038032Speter { 13164562Sgshapiro a->q_state = QS_VERIFIED; 13238032Speter return; 13338032Speter } 13438032Speter message("aliased to %s", shortenstring(p, MAXSHORTSTR)); 13564562Sgshapiro if (LogLevel > 10) 13638032Speter sm_syslog(LOG_INFO, e->e_id, 13790792Sgshapiro "alias %.100s => %s", 13890792Sgshapiro a->q_paddr, shortenstring(p, MAXSHORTSTR)); 13938032Speter a->q_flags &= ~QSELFREF; 14038032Speter if (tTd(27, 5)) 14138032Speter { 14290792Sgshapiro sm_dprintf("alias: QS_EXPANDED "); 143132943Sgshapiro printaddr(sm_debug_file(), a, false); 14438032Speter } 14564562Sgshapiro a->q_state = QS_EXPANDED; 14664562Sgshapiro 14764562Sgshapiro /* 14864562Sgshapiro ** Always deliver aliased items as the default user. 14964562Sgshapiro ** Setting q_gid to 0 forces deliver() to use DefUser 15064562Sgshapiro ** instead of the alias name for the call to initgroups(). 15164562Sgshapiro */ 15264562Sgshapiro 15364562Sgshapiro a->q_uid = DefUid; 15464562Sgshapiro a->q_gid = 0; 15564562Sgshapiro a->q_fullname = NULL; 15664562Sgshapiro a->q_flags |= QGOODUID|QALIAS; 15764562Sgshapiro 15838032Speter (void) sendtolist(p, a, sendq, aliaslevel + 1, e); 15990792Sgshapiro 16064562Sgshapiro if (bitset(QSELFREF, a->q_flags) && QS_IS_EXPANDED(a->q_state)) 16164562Sgshapiro a->q_state = QS_OK; 16238032Speter 16338032Speter /* 16438032Speter ** Look for owner of alias 16538032Speter */ 16638032Speter 16738032Speter if (strncmp(a->q_user, "owner-", 6) == 0 || 168168515Sgshapiro strlen(a->q_user) > sizeof(obuf) - 7) 169168515Sgshapiro (void) sm_strlcpy(obuf, "owner-owner", sizeof(obuf)); 17038032Speter else 171168515Sgshapiro (void) sm_strlcpyn(obuf, sizeof(obuf), 2, "owner-", a->q_user); 17290792Sgshapiro owner = aliaslookup(obuf, &status, a->q_host); 17338032Speter if (owner == NULL) 17438032Speter return; 17538032Speter 17638032Speter /* reflect owner into envelope sender */ 17738032Speter if (strpbrk(owner, ",:/|\"") != NULL) 17838032Speter owner = obuf; 17990792Sgshapiro a->q_owner = sm_rpool_strdup_x(e->e_rpool, owner); 18038032Speter 18138032Speter /* announce delivery to this alias; NORECEIPT bit set later */ 18238032Speter if (e->e_xfp != NULL) 18390792Sgshapiro (void) sm_io_fprintf(e->e_xfp, SM_TIME_DEFAULT, 18490792Sgshapiro "Message delivered to mailing list %s\n", 18590792Sgshapiro a->q_paddr); 18638032Speter e->e_flags |= EF_SENDRECEIPT; 18738032Speter a->q_flags |= QDELIVERED|QEXPANDED; 18838032Speter} 18990792Sgshapiro/* 19038032Speter** ALIASLOOKUP -- look up a name in the alias file. 19138032Speter** 19238032Speter** Parameters: 19338032Speter** name -- the name to look up. 19438032Speter** pstat -- a pointer to a place to put the status. 19590792Sgshapiro** av -- argument for %1 expansion. 19638032Speter** 19738032Speter** Returns: 19838032Speter** the value of name. 19938032Speter** NULL if unknown. 20038032Speter** 20138032Speter** Side Effects: 20238032Speter** none. 20338032Speter** 20438032Speter** Warnings: 20538032Speter** The return value will be trashed across calls. 20638032Speter*/ 20738032Speter 20864562Sgshapirostatic char * 20990792Sgshapiroaliaslookup(name, pstat, av) 21038032Speter char *name; 21138032Speter int *pstat; 21290792Sgshapiro char *av; 21338032Speter{ 21438032Speter static MAP *map = NULL; 21590792Sgshapiro#if _FFR_ALIAS_DETAIL 21690792Sgshapiro int i; 21790792Sgshapiro char *argv[4]; 21890792Sgshapiro#endif /* _FFR_ALIAS_DETAIL */ 21938032Speter 22038032Speter if (map == NULL) 22138032Speter { 22238032Speter STAB *s = stab("aliases", ST_MAP, ST_FIND); 22338032Speter 22438032Speter if (s == NULL) 22538032Speter return NULL; 22638032Speter map = &s->s_map; 22738032Speter } 22864562Sgshapiro DYNOPENMAP(map); 22938032Speter 23038032Speter /* special case POstMastER -- always use lower case */ 23190792Sgshapiro if (sm_strcasecmp(name, "postmaster") == 0) 23238032Speter name = "postmaster"; 23338032Speter 23490792Sgshapiro#if _FFR_ALIAS_DETAIL 23590792Sgshapiro i = 0; 23690792Sgshapiro argv[i++] = name; 23790792Sgshapiro argv[i++] = av; 23890792Sgshapiro 23990792Sgshapiro /* XXX '+' is hardwired here as delimiter! */ 24090792Sgshapiro if (av != NULL && *av == '+') 24190792Sgshapiro argv[i++] = av + 1; 24290792Sgshapiro argv[i++] = NULL; 24390792Sgshapiro return (*map->map_class->map_lookup)(map, name, argv, pstat); 24490792Sgshapiro#else /* _FFR_ALIAS_DETAIL */ 24538032Speter return (*map->map_class->map_lookup)(map, name, NULL, pstat); 24690792Sgshapiro#endif /* _FFR_ALIAS_DETAIL */ 24738032Speter} 24890792Sgshapiro/* 24938032Speter** SETALIAS -- set up an alias map 25038032Speter** 25138032Speter** Called when reading configuration file. 25238032Speter** 25338032Speter** Parameters: 25438032Speter** spec -- the alias specification 25538032Speter** 25638032Speter** Returns: 25738032Speter** none. 25838032Speter*/ 25938032Speter 26038032Spetervoid 26138032Spetersetalias(spec) 26238032Speter char *spec; 26338032Speter{ 26438032Speter register char *p; 26538032Speter register MAP *map; 26638032Speter char *class; 26738032Speter STAB *s; 26838032Speter 26938032Speter if (tTd(27, 8)) 27090792Sgshapiro sm_dprintf("setalias(%s)\n", spec); 27138032Speter 27238032Speter for (p = spec; p != NULL; ) 27338032Speter { 27438032Speter char buf[50]; 27538032Speter 27638032Speter while (isascii(*p) && isspace(*p)) 27738032Speter p++; 27838032Speter if (*p == '\0') 27938032Speter break; 28038032Speter spec = p; 28138032Speter 28238032Speter if (NAliasFileMaps >= MAXMAPSTACK) 28338032Speter { 28438032Speter syserr("Too many alias databases defined, %d max", 28538032Speter MAXMAPSTACK); 28638032Speter return; 28738032Speter } 28838032Speter if (AliasFileMap == NULL) 28938032Speter { 29090792Sgshapiro (void) sm_strlcpy(buf, "aliases.files sequence", 291168515Sgshapiro sizeof(buf)); 29238032Speter AliasFileMap = makemapentry(buf); 29338032Speter if (AliasFileMap == NULL) 29438032Speter { 29538032Speter syserr("setalias: cannot create aliases.files map"); 29638032Speter return; 29738032Speter } 29838032Speter } 299168515Sgshapiro (void) sm_snprintf(buf, sizeof(buf), "Alias%d", NAliasFileMaps); 30038032Speter s = stab(buf, ST_MAP, ST_ENTER); 30138032Speter map = &s->s_map; 302168515Sgshapiro memset(map, '\0', sizeof(*map)); 30338032Speter map->map_mname = s->s_name; 30471345Sgshapiro p = strpbrk(p, ALIAS_SPEC_SEPARATORS); 30564562Sgshapiro if (p != NULL && *p == SEPARATOR) 30638032Speter { 30738032Speter /* map name */ 30838032Speter *p++ = '\0'; 30938032Speter class = spec; 31038032Speter spec = p; 31138032Speter } 31238032Speter else 31338032Speter { 31438032Speter class = "implicit"; 31538032Speter map->map_mflags = MF_INCLNULL; 31638032Speter } 31738032Speter 31838032Speter /* find end of spec */ 31938032Speter if (p != NULL) 32064562Sgshapiro { 32190792Sgshapiro bool quoted = false; 32264562Sgshapiro 32364562Sgshapiro for (; *p != '\0'; p++) 32464562Sgshapiro { 32564562Sgshapiro /* 32664562Sgshapiro ** Don't break into a quoted string. 32764562Sgshapiro ** Needed for ldap maps which use 32864562Sgshapiro ** commas in their specifications. 32964562Sgshapiro */ 33064562Sgshapiro 33164562Sgshapiro if (*p == '"') 33264562Sgshapiro quoted = !quoted; 33364562Sgshapiro else if (*p == ',' && !quoted) 33464562Sgshapiro break; 33564562Sgshapiro } 33664562Sgshapiro 33764562Sgshapiro /* No more alias specifications follow */ 33864562Sgshapiro if (*p == '\0') 33964562Sgshapiro p = NULL; 34064562Sgshapiro } 34138032Speter if (p != NULL) 34238032Speter *p++ = '\0'; 34338032Speter 34438032Speter if (tTd(27, 20)) 34590792Sgshapiro sm_dprintf(" map %s:%s %s\n", class, s->s_name, spec); 34638032Speter 34738032Speter /* look up class */ 34838032Speter s = stab(class, ST_MAPCLASS, ST_FIND); 34938032Speter if (s == NULL) 35038032Speter { 35138032Speter syserr("setalias: unknown alias class %s", class); 35238032Speter } 35338032Speter else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags)) 35438032Speter { 35538032Speter syserr("setalias: map class %s can't handle aliases", 35638032Speter class); 35738032Speter } 35838032Speter else 35938032Speter { 36038032Speter map->map_class = &s->s_mapclass; 36190792Sgshapiro map->map_mflags |= MF_ALIAS; 36238032Speter if (map->map_class->map_parse(map, spec)) 36338032Speter { 36490792Sgshapiro map->map_mflags |= MF_VALID; 36538032Speter AliasFileMap->map_stack[NAliasFileMaps++] = map; 36638032Speter } 36738032Speter } 36838032Speter } 36938032Speter} 37090792Sgshapiro/* 37138032Speter** ALIASWAIT -- wait for distinguished @:@ token to appear. 37238032Speter** 37338032Speter** This can decide to reopen or rebuild the alias file 37438032Speter** 37538032Speter** Parameters: 37638032Speter** map -- a pointer to the map descriptor for this alias file. 37738032Speter** ext -- the filename extension (e.g., ".db") for the 37838032Speter** database file. 37938032Speter** isopen -- if set, the database is already open, and we 38038032Speter** should check for validity; otherwise, we are 38138032Speter** just checking to see if it should be created. 38238032Speter** 38338032Speter** Returns: 38490792Sgshapiro** true -- if the database is open when we return. 38590792Sgshapiro** false -- if the database is closed when we return. 38638032Speter*/ 38738032Speter 38838032Speterbool 38938032Speteraliaswait(map, ext, isopen) 39038032Speter MAP *map; 39138032Speter char *ext; 39264562Sgshapiro bool isopen; 39338032Speter{ 39490792Sgshapiro bool attimeout = false; 39538032Speter time_t mtime; 39638032Speter struct stat stb; 39798121Sgshapiro char buf[MAXPATHLEN]; 39838032Speter 39938032Speter if (tTd(27, 3)) 40090792Sgshapiro sm_dprintf("aliaswait(%s:%s)\n", 40190792Sgshapiro map->map_class->map_cname, map->map_file); 40238032Speter if (bitset(MF_ALIASWAIT, map->map_mflags)) 40338032Speter return isopen; 40438032Speter map->map_mflags |= MF_ALIASWAIT; 40538032Speter 40638032Speter if (SafeAlias > 0) 40738032Speter { 40838032Speter auto int st; 40990792Sgshapiro unsigned int sleeptime = 2; 41090792Sgshapiro unsigned int loopcount = 0; /* only used for debugging */ 41138032Speter time_t toolong = curtime() + SafeAlias; 41238032Speter 41338032Speter while (isopen && 41438032Speter map->map_class->map_lookup(map, "@", NULL, &st) == NULL) 41538032Speter { 41638032Speter if (curtime() > toolong) 41738032Speter { 41838032Speter /* we timed out */ 41990792Sgshapiro attimeout = true; 42038032Speter break; 42138032Speter } 42238032Speter 42338032Speter /* 42438032Speter ** Close and re-open the alias database in case 42538032Speter ** the one is mv'ed instead of cp'ed in. 42638032Speter */ 42738032Speter 42838032Speter if (tTd(27, 2)) 42990792Sgshapiro { 43090792Sgshapiro loopcount++; 43190792Sgshapiro sm_dprintf("aliaswait: sleeping for %u seconds (loopcount = %u)\n", 43290792Sgshapiro sleeptime, loopcount); 43390792Sgshapiro } 43438032Speter 43577349Sgshapiro map->map_mflags |= MF_CLOSING; 43638032Speter map->map_class->map_close(map); 43777349Sgshapiro map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 43864562Sgshapiro (void) sleep(sleeptime); 43938032Speter sleeptime *= 2; 44038032Speter if (sleeptime > 60) 44138032Speter sleeptime = 60; 44238032Speter isopen = map->map_class->map_open(map, O_RDONLY); 44338032Speter } 44438032Speter } 44538032Speter 44638032Speter /* see if we need to go into auto-rebuild mode */ 44738032Speter if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 44838032Speter { 44938032Speter if (tTd(27, 3)) 45090792Sgshapiro sm_dprintf("aliaswait: not rebuildable\n"); 45138032Speter map->map_mflags &= ~MF_ALIASWAIT; 45238032Speter return isopen; 45338032Speter } 45438032Speter if (stat(map->map_file, &stb) < 0) 45538032Speter { 45638032Speter if (tTd(27, 3)) 45790792Sgshapiro sm_dprintf("aliaswait: no source file\n"); 45838032Speter map->map_mflags &= ~MF_ALIASWAIT; 45938032Speter return isopen; 46038032Speter } 46138032Speter mtime = stb.st_mtime; 462168515Sgshapiro if (sm_strlcpyn(buf, sizeof(buf), 2, 463168515Sgshapiro map->map_file, ext == NULL ? "" : ext) >= sizeof(buf)) 46498121Sgshapiro { 46598121Sgshapiro if (LogLevel > 3) 46698121Sgshapiro sm_syslog(LOG_INFO, NOQID, 46798121Sgshapiro "alias database %s%s name too long", 46898121Sgshapiro map->map_file, ext == NULL ? "" : ext); 46998121Sgshapiro message("alias database %s%s name too long", 47098121Sgshapiro map->map_file, ext == NULL ? "" : ext); 47198121Sgshapiro } 47298121Sgshapiro 47338032Speter if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout) 47438032Speter { 47564562Sgshapiro if (LogLevel > 3) 47664562Sgshapiro sm_syslog(LOG_INFO, NOQID, 47790792Sgshapiro "alias database %s out of date", buf); 47864562Sgshapiro message("Warning: alias database %s out of date", buf); 47938032Speter } 48038032Speter map->map_mflags &= ~MF_ALIASWAIT; 48138032Speter return isopen; 48238032Speter} 48390792Sgshapiro/* 48438032Speter** REBUILDALIASES -- rebuild the alias database. 48538032Speter** 48638032Speter** Parameters: 48738032Speter** map -- the database to rebuild. 48838032Speter** automatic -- set if this was automatically generated. 48938032Speter** 49038032Speter** Returns: 49190792Sgshapiro** true if successful; false otherwise. 49238032Speter** 49338032Speter** Side Effects: 49438032Speter** Reads the text version of the database, builds the 49538032Speter** DBM or DB version. 49638032Speter*/ 49738032Speter 49838032Speterbool 49938032Speterrebuildaliases(map, automatic) 50038032Speter register MAP *map; 50138032Speter bool automatic; 50238032Speter{ 50390792Sgshapiro SM_FILE_T *af; 50490792Sgshapiro bool nolock = false; 50590792Sgshapiro bool success = false; 50664562Sgshapiro long sff = SFF_OPENASROOT|SFF_REGONLY|SFF_NOLOCK; 50738032Speter sigfunc_t oldsigint, oldsigquit; 50838032Speter#ifdef SIGTSTP 50938032Speter sigfunc_t oldsigtstp; 51064562Sgshapiro#endif /* SIGTSTP */ 51138032Speter 51238032Speter if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 51390792Sgshapiro return false; 51438032Speter 51564562Sgshapiro if (!bitnset(DBS_LINKEDALIASFILEINWRITABLEDIR, DontBlameSendmail)) 51638032Speter sff |= SFF_NOWLINK; 51764562Sgshapiro if (!bitnset(DBS_GROUPWRITABLEALIASFILE, DontBlameSendmail)) 51838032Speter sff |= SFF_NOGWFILES; 51964562Sgshapiro if (!bitnset(DBS_WORLDWRITABLEALIASFILE, DontBlameSendmail)) 52038032Speter sff |= SFF_NOWWFILES; 52138032Speter 52238032Speter /* try to lock the source file */ 52338032Speter if ((af = safefopen(map->map_file, O_RDWR, 0, sff)) == NULL) 52438032Speter { 52538032Speter struct stat stb; 52638032Speter 52738032Speter if ((errno != EACCES && errno != EROFS) || automatic || 52838032Speter (af = safefopen(map->map_file, O_RDONLY, 0, sff)) == NULL) 52938032Speter { 53038032Speter int saveerr = errno; 53138032Speter 53238032Speter if (tTd(27, 1)) 53390792Sgshapiro sm_dprintf("Can't open %s: %s\n", 53490792Sgshapiro map->map_file, sm_errstring(saveerr)); 53538032Speter if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags)) 53638032Speter message("newaliases: cannot open %s: %s", 53790792Sgshapiro map->map_file, sm_errstring(saveerr)); 53838032Speter errno = 0; 53990792Sgshapiro return false; 54038032Speter } 54190792Sgshapiro nolock = true; 54238032Speter if (tTd(27, 1) || 54390792Sgshapiro fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &stb) < 0 || 54438032Speter bitset(S_IWUSR|S_IWGRP|S_IWOTH, stb.st_mode)) 54538032Speter message("warning: cannot lock %s: %s", 54690792Sgshapiro map->map_file, sm_errstring(errno)); 54738032Speter } 54838032Speter 54938032Speter /* see if someone else is rebuilding the alias file */ 55038032Speter if (!nolock && 55190792Sgshapiro !lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), map->map_file, 55290792Sgshapiro NULL, LOCK_EX|LOCK_NB)) 55338032Speter { 55438032Speter /* yes, they are -- wait until done */ 55538032Speter message("Alias file %s is locked (maybe being rebuilt)", 55638032Speter map->map_file); 55738032Speter if (OpMode != MD_INITALIAS) 55838032Speter { 55938032Speter /* wait for other rebuild to complete */ 56090792Sgshapiro (void) lockfile(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), 56190792Sgshapiro map->map_file, NULL, LOCK_EX); 56238032Speter } 56390792Sgshapiro (void) sm_io_close(af, SM_TIME_DEFAULT); 56438032Speter errno = 0; 56590792Sgshapiro return false; 56638032Speter } 56738032Speter 56890792Sgshapiro oldsigint = sm_signal(SIGINT, SIG_IGN); 56990792Sgshapiro oldsigquit = sm_signal(SIGQUIT, SIG_IGN); 57038032Speter#ifdef SIGTSTP 57190792Sgshapiro oldsigtstp = sm_signal(SIGTSTP, SIG_IGN); 57264562Sgshapiro#endif /* SIGTSTP */ 57338032Speter 57438032Speter if (map->map_class->map_open(map, O_RDWR)) 57538032Speter { 57638032Speter if (LogLevel > 7) 57738032Speter { 57838032Speter sm_syslog(LOG_NOTICE, NOQID, 57938032Speter "alias database %s %srebuilt by %s", 58038032Speter map->map_file, automatic ? "auto" : "", 58138032Speter username()); 58238032Speter } 58338032Speter map->map_mflags |= MF_OPEN|MF_WRITABLE; 58490792Sgshapiro map->map_pid = CurrentPid; 58590792Sgshapiro readaliases(map, af, !automatic, true); 58690792Sgshapiro success = true; 58738032Speter } 58838032Speter else 58938032Speter { 59038032Speter if (tTd(27, 1)) 59190792Sgshapiro sm_dprintf("Can't create database for %s: %s\n", 59290792Sgshapiro map->map_file, sm_errstring(errno)); 59338032Speter if (!automatic) 59438032Speter syserr("Cannot create database for alias file %s", 59538032Speter map->map_file); 59638032Speter } 59738032Speter 59838032Speter /* close the file, thus releasing locks */ 59990792Sgshapiro (void) sm_io_close(af, SM_TIME_DEFAULT); 60038032Speter 60138032Speter /* add distinguished entries and close the database */ 60238032Speter if (bitset(MF_OPEN, map->map_mflags)) 60338032Speter { 60477349Sgshapiro map->map_mflags |= MF_CLOSING; 60538032Speter map->map_class->map_close(map); 60677349Sgshapiro map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 60738032Speter } 60838032Speter 60938032Speter /* restore the old signals */ 61090792Sgshapiro (void) sm_signal(SIGINT, oldsigint); 61190792Sgshapiro (void) sm_signal(SIGQUIT, oldsigquit); 61290792Sgshapiro#ifdef SIGTSTP 61390792Sgshapiro (void) sm_signal(SIGTSTP, oldsigtstp); 61490792Sgshapiro#endif /* SIGTSTP */ 61538032Speter return success; 61638032Speter} 61790792Sgshapiro/* 61838032Speter** READALIASES -- read and process the alias file. 61938032Speter** 62038032Speter** This routine implements the part of initaliases that occurs 62138032Speter** when we are not going to use the DBM stuff. 62238032Speter** 62338032Speter** Parameters: 62438032Speter** map -- the alias database descriptor. 62538032Speter** af -- file to read the aliases from. 62664562Sgshapiro** announcestats -- announce statistics regarding number of 62738032Speter** aliases, longest alias, etc. 62838032Speter** logstats -- lot the same info. 62938032Speter** 63038032Speter** Returns: 63138032Speter** none. 63238032Speter** 63338032Speter** Side Effects: 63438032Speter** Reads aliasfile into the symbol table. 63538032Speter** Optionally, builds the .dir & .pag files. 63638032Speter*/ 63738032Speter 63838032Spetervoid 63938032Speterreadaliases(map, af, announcestats, logstats) 64038032Speter register MAP *map; 64190792Sgshapiro SM_FILE_T *af; 64238032Speter bool announcestats; 64338032Speter bool logstats; 64438032Speter{ 64538032Speter register char *p; 64638032Speter char *rhs; 64738032Speter bool skipping; 64838032Speter long naliases, bytes, longest; 64938032Speter ADDRESS al, bl; 65038032Speter char line[BUFSIZ]; 65138032Speter 65238032Speter /* 65338032Speter ** Read and interpret lines 65438032Speter */ 65538032Speter 65638032Speter FileName = map->map_file; 65738032Speter LineNumber = 0; 65838032Speter naliases = bytes = longest = 0; 65990792Sgshapiro skipping = false; 660249729Sgshapiro while (sm_io_fgets(af, SM_TIME_DEFAULT, line, sizeof(line)) >= 0) 66138032Speter { 66238032Speter int lhssize, rhssize; 66338032Speter int c; 66438032Speter 66538032Speter LineNumber++; 66638032Speter p = strchr(line, '\n'); 66790792Sgshapiro 66890792Sgshapiro /* XXX what if line="a\\" ? */ 66938032Speter while (p != NULL && p > line && p[-1] == '\\') 67038032Speter { 67138032Speter p--; 67290792Sgshapiro if (sm_io_fgets(af, SM_TIME_DEFAULT, p, 673249729Sgshapiro SPACELEFT(line, p)) < 0) 67438032Speter break; 67538032Speter LineNumber++; 67638032Speter p = strchr(p, '\n'); 67738032Speter } 67838032Speter if (p != NULL) 67938032Speter *p = '\0'; 68090792Sgshapiro else if (!sm_io_eof(af)) 68138032Speter { 68264562Sgshapiro errno = 0; 68364562Sgshapiro syserr("554 5.3.0 alias line too long"); 68438032Speter 68538032Speter /* flush to end of line */ 68690792Sgshapiro while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) != 68790792Sgshapiro SM_IO_EOF && c != '\n') 68838032Speter continue; 68938032Speter 69038032Speter /* skip any continuation lines */ 69190792Sgshapiro skipping = true; 69238032Speter continue; 69338032Speter } 69438032Speter switch (line[0]) 69538032Speter { 69638032Speter case '#': 69738032Speter case '\0': 69890792Sgshapiro skipping = false; 69938032Speter continue; 70038032Speter 70138032Speter case ' ': 70238032Speter case '\t': 70338032Speter if (!skipping) 70464562Sgshapiro syserr("554 5.3.5 Non-continuation line starts with space"); 70590792Sgshapiro skipping = true; 70638032Speter continue; 70738032Speter } 70890792Sgshapiro skipping = false; 70938032Speter 71038032Speter /* 71138032Speter ** Process the LHS 71238032Speter ** Find the colon separator, and parse the address. 71338032Speter ** It should resolve to a local name -- this will 71438032Speter ** be checked later (we want to optionally do 71538032Speter ** parsing of the RHS first to maximize error 71638032Speter ** detection). 71738032Speter */ 71838032Speter 71938032Speter for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 72038032Speter continue; 72138032Speter if (*p++ != ':') 72238032Speter { 72364562Sgshapiro syserr("554 5.3.5 missing colon"); 72438032Speter continue; 72538032Speter } 72690792Sgshapiro if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv, true) 72790792Sgshapiro == NULL) 72838032Speter { 72964562Sgshapiro syserr("554 5.3.5 %.40s... illegal alias name", line); 73038032Speter continue; 73138032Speter } 73238032Speter 73338032Speter /* 73438032Speter ** Process the RHS. 73538032Speter ** 'al' is the internal form of the LHS address. 73638032Speter ** 'p' points to the text of the RHS. 73738032Speter */ 73838032Speter 73938032Speter while (isascii(*p) && isspace(*p)) 74038032Speter p++; 74138032Speter rhs = p; 74238032Speter for (;;) 74338032Speter { 74438032Speter register char *nlp; 74538032Speter 74638032Speter nlp = &p[strlen(p)]; 74771345Sgshapiro if (nlp > p && nlp[-1] == '\n') 74838032Speter *--nlp = '\0'; 74938032Speter 75038032Speter if (CheckAliases) 75138032Speter { 75238032Speter /* do parsing & compression of addresses */ 75338032Speter while (*p != '\0') 75438032Speter { 75538032Speter auto char *delimptr; 75638032Speter 75738032Speter while ((isascii(*p) && isspace(*p)) || 75838032Speter *p == ',') 75938032Speter p++; 76038032Speter if (*p == '\0') 76138032Speter break; 76238032Speter if (parseaddr(p, &bl, RF_COPYNONE, ',', 76390792Sgshapiro &delimptr, CurEnv, true) 76490792Sgshapiro == NULL) 76564562Sgshapiro usrerr("553 5.3.5 %s... bad address", p); 76638032Speter p = delimptr; 76738032Speter } 76838032Speter } 76938032Speter else 77038032Speter { 77138032Speter p = nlp; 77238032Speter } 77338032Speter 77438032Speter /* see if there should be a continuation line */ 77590792Sgshapiro c = sm_io_getc(af, SM_TIME_DEFAULT); 77690792Sgshapiro if (!sm_io_eof(af)) 77790792Sgshapiro (void) sm_io_ungetc(af, SM_TIME_DEFAULT, c); 77838032Speter if (c != ' ' && c != '\t') 77938032Speter break; 78038032Speter 78138032Speter /* read continuation line */ 78290792Sgshapiro if (sm_io_fgets(af, SM_TIME_DEFAULT, p, 783249729Sgshapiro sizeof(line) - (p-line)) < 0) 78438032Speter break; 78538032Speter LineNumber++; 78638032Speter 78738032Speter /* check for line overflow */ 78890792Sgshapiro if (strchr(p, '\n') == NULL && !sm_io_eof(af)) 78938032Speter { 79064562Sgshapiro usrerr("554 5.3.5 alias too long"); 79190792Sgshapiro while ((c = sm_io_getc(af, SM_TIME_DEFAULT)) 79290792Sgshapiro != SM_IO_EOF && c != '\n') 79338032Speter continue; 79490792Sgshapiro skipping = true; 79538032Speter break; 79638032Speter } 79738032Speter } 79838032Speter 79938032Speter if (skipping) 80038032Speter continue; 80138032Speter 80238032Speter if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags)) 80338032Speter { 80464562Sgshapiro syserr("554 5.3.5 %s... cannot alias non-local names", 80538032Speter al.q_paddr); 80638032Speter continue; 80738032Speter } 80838032Speter 80938032Speter /* 81038032Speter ** Insert alias into symbol table or database file. 81138032Speter ** 81238032Speter ** Special case pOStmaStER -- always make it lower case. 81338032Speter */ 81438032Speter 81590792Sgshapiro if (sm_strcasecmp(al.q_user, "postmaster") == 0) 81638032Speter makelower(al.q_user); 81738032Speter 81838032Speter lhssize = strlen(al.q_user); 81938032Speter rhssize = strlen(rhs); 82064562Sgshapiro if (rhssize > 0) 82164562Sgshapiro { 82264562Sgshapiro /* is RHS empty (just spaces)? */ 82364562Sgshapiro p = rhs; 82464562Sgshapiro while (isascii(*p) && isspace(*p)) 82564562Sgshapiro p++; 82664562Sgshapiro } 82764562Sgshapiro if (rhssize == 0 || *p == '\0') 82864562Sgshapiro { 82964562Sgshapiro syserr("554 5.3.5 %.40s... missing value for alias", 83064562Sgshapiro line); 83138032Speter 83264562Sgshapiro } 83364562Sgshapiro else 83464562Sgshapiro { 83564562Sgshapiro map->map_class->map_store(map, al.q_user, rhs); 83664562Sgshapiro 83764562Sgshapiro /* statistics */ 83864562Sgshapiro naliases++; 83964562Sgshapiro bytes += lhssize + rhssize; 84064562Sgshapiro if (rhssize > longest) 84164562Sgshapiro longest = rhssize; 84264562Sgshapiro } 84364562Sgshapiro 84490792Sgshapiro#if 0 84590792Sgshapiro /* 84690792Sgshapiro ** address strings are now stored in the envelope rpool, 84790792Sgshapiro ** and therefore cannot be freed. 84890792Sgshapiro */ 84938032Speter if (al.q_paddr != NULL) 85090792Sgshapiro sm_free(al.q_paddr); /* disabled */ 85138032Speter if (al.q_host != NULL) 85290792Sgshapiro sm_free(al.q_host); /* disabled */ 85338032Speter if (al.q_user != NULL) 85490792Sgshapiro sm_free(al.q_user); /* disabled */ 85590792Sgshapiro#endif /* 0 */ 85638032Speter } 85738032Speter 85838032Speter CurEnv->e_to = NULL; 85938032Speter FileName = NULL; 86038032Speter if (Verbose || announcestats) 86166494Sgshapiro message("%s: %ld aliases, longest %ld bytes, %ld bytes total", 86238032Speter map->map_file, naliases, longest, bytes); 86338032Speter if (LogLevel > 7 && logstats) 86438032Speter sm_syslog(LOG_INFO, NOQID, 86566494Sgshapiro "%s: %ld aliases, longest %ld bytes, %ld bytes total", 86638032Speter map->map_file, naliases, longest, bytes); 86738032Speter} 86890792Sgshapiro/* 86938032Speter** FORWARD -- Try to forward mail 87038032Speter** 87138032Speter** This is similar but not identical to aliasing. 87238032Speter** 87338032Speter** Parameters: 87438032Speter** user -- the name of the user who's mail we would like 87538032Speter** to forward to. It must have been verified -- 87638032Speter** i.e., the q_home field must have been filled 87738032Speter** in. 87838032Speter** sendq -- a pointer to the head of the send queue to 87938032Speter** put this user's aliases in. 88038032Speter** aliaslevel -- the current alias nesting depth. 88138032Speter** e -- the current envelope. 88238032Speter** 88338032Speter** Returns: 88438032Speter** none. 88538032Speter** 88638032Speter** Side Effects: 88738032Speter** New names are added to send queues. 88838032Speter*/ 88938032Speter 89038032Spetervoid 89138032Speterforward(user, sendq, aliaslevel, e) 89238032Speter ADDRESS *user; 89338032Speter ADDRESS **sendq; 89438032Speter int aliaslevel; 89538032Speter register ENVELOPE *e; 89638032Speter{ 89738032Speter char *pp; 89838032Speter char *ep; 89938032Speter bool got_transient; 90038032Speter 90138032Speter if (tTd(27, 1)) 90290792Sgshapiro sm_dprintf("forward(%s)\n", user->q_paddr); 90338032Speter 90438032Speter if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) || 90564562Sgshapiro !QS_IS_OK(user->q_state)) 90638032Speter return; 90790792Sgshapiro if (ForwardPath != NULL && *ForwardPath == '\0') 90890792Sgshapiro return; 90938032Speter if (user->q_home == NULL) 91038032Speter { 91164562Sgshapiro syserr("554 5.3.0 forward: no home"); 91238032Speter user->q_home = "/no/such/directory"; 91338032Speter } 91438032Speter 91538032Speter /* good address -- look for .forward file in home */ 91690792Sgshapiro macdefine(&e->e_macro, A_PERM, 'z', user->q_home); 91790792Sgshapiro macdefine(&e->e_macro, A_PERM, 'u', user->q_user); 91890792Sgshapiro macdefine(&e->e_macro, A_PERM, 'h', user->q_host); 91938032Speter if (ForwardPath == NULL) 92038032Speter ForwardPath = newstr("\201z/.forward"); 92138032Speter 92290792Sgshapiro got_transient = false; 92338032Speter for (pp = ForwardPath; pp != NULL; pp = ep) 92438032Speter { 92538032Speter int err; 92698121Sgshapiro char buf[MAXPATHLEN]; 92764562Sgshapiro struct stat st; 92838032Speter 92964562Sgshapiro ep = strchr(pp, SEPARATOR); 93038032Speter if (ep != NULL) 93138032Speter *ep = '\0'; 932168515Sgshapiro expand(pp, buf, sizeof(buf), e); 93338032Speter if (ep != NULL) 93464562Sgshapiro *ep++ = SEPARATOR; 93538032Speter if (buf[0] == '\0') 93638032Speter continue; 93738032Speter if (tTd(27, 3)) 93890792Sgshapiro sm_dprintf("forward: trying %s\n", buf); 93938032Speter 94090792Sgshapiro err = include(buf, true, user, sendq, aliaslevel, e); 94138032Speter if (err == 0) 94238032Speter break; 94338032Speter else if (transienterror(err)) 94438032Speter { 94538032Speter /* we may have to suspend this message */ 94690792Sgshapiro got_transient = true; 94738032Speter if (tTd(27, 2)) 94890792Sgshapiro sm_dprintf("forward: transient error on %s\n", 94990792Sgshapiro buf); 95038032Speter if (LogLevel > 2) 95164562Sgshapiro { 95264562Sgshapiro char *curhost = CurHostName; 95364562Sgshapiro 95464562Sgshapiro CurHostName = NULL; 95538032Speter sm_syslog(LOG_ERR, e->e_id, 95664562Sgshapiro "forward %s: transient error: %s", 95790792Sgshapiro buf, sm_errstring(err)); 95864562Sgshapiro CurHostName = curhost; 95964562Sgshapiro } 96064562Sgshapiro 96138032Speter } 96238032Speter else 96338032Speter { 96438032Speter switch (err) 96538032Speter { 96638032Speter case ENOENT: 96738032Speter break; 96838032Speter 96964562Sgshapiro case E_SM_WWDIR: 97064562Sgshapiro case E_SM_GWDIR: 97164562Sgshapiro /* check if it even exists */ 97264562Sgshapiro if (stat(buf, &st) < 0 && errno == ENOENT) 97364562Sgshapiro { 97464562Sgshapiro if (bitnset(DBS_DONTWARNFORWARDFILEINUNSAFEDIRPATH, 97564562Sgshapiro DontBlameSendmail)) 97664562Sgshapiro break; 97764562Sgshapiro } 97864562Sgshapiro /* FALLTHROUGH */ 97964562Sgshapiro 98038032Speter#if _FFR_FORWARD_SYSERR 98138032Speter case E_SM_NOSLINK: 98238032Speter case E_SM_NOHLINK: 98338032Speter case E_SM_REGONLY: 98438032Speter case E_SM_ISEXEC: 98538032Speter case E_SM_WWFILE: 98638032Speter case E_SM_GWFILE: 98790792Sgshapiro syserr("forward: %s: %s", buf, sm_errstring(err)); 98838032Speter break; 98964562Sgshapiro#endif /* _FFR_FORWARD_SYSERR */ 99038032Speter 99138032Speter default: 99238032Speter if (LogLevel > (RunAsUid == 0 ? 2 : 10)) 99338032Speter sm_syslog(LOG_WARNING, e->e_id, 99490792Sgshapiro "forward %s: %s", buf, 99590792Sgshapiro sm_errstring(err)); 99638032Speter if (Verbose) 99738032Speter message("forward: %s: %s", 99890792Sgshapiro buf, sm_errstring(err)); 99938032Speter break; 100038032Speter } 100138032Speter } 100238032Speter } 100338032Speter if (pp == NULL && got_transient) 100438032Speter { 100538032Speter /* 100638032Speter ** There was no successful .forward open and at least one 100738032Speter ** transient open. We have to defer this address for 100838032Speter ** further delivery. 100938032Speter */ 101038032Speter 101138032Speter message("transient .forward open error: message queued"); 101264562Sgshapiro user->q_state = QS_QUEUEUP; 101338032Speter return; 101438032Speter } 101538032Speter} 1016