#include <pwd.h> #include <time.h> #include "sendmail.h" #include <sys/stat.h> SCCSID(@(#)envelope.c 4.2 8/21/83); /* ** NEWENVELOPE -- allocate a new envelope ** ** Supports inheritance. ** ** Parameters: ** e -- the new envelope to fill in. ** ** Returns: ** e. ** ** Side Effects: ** none. */ ENVELOPE * newenvelope(e) register ENVELOPE *e; { register HDR *bh; register HDR **nhp; register ENVELOPE *parent; extern putheader(), putbody(); extern ENVELOPE BlankEnvelope; parent = CurEnv; if (e == CurEnv) parent = e->e_parent; clear((char *) e, sizeof *e); bmove((char *) &CurEnv->e_from, (char *) &e->e_from, sizeof e->e_from); e->e_parent = parent; e->e_ctime = curtime(); e->e_puthdr = putheader; e->e_putbody = putbody; bh = BlankEnvelope.e_header; nhp = &e->e_header; while (bh != NULL) { *nhp = (HDR *) xalloc(sizeof *bh); bmove((char *) bh, (char *) *nhp, sizeof *bh); bh = bh->h_link; nhp = &(*nhp)->h_link; } if (CurEnv->e_xfp != NULL) (void) fflush(CurEnv->e_xfp); return (e); } /* ** DROPENVELOPE -- deallocate an envelope. ** ** Parameters: ** e -- the envelope to deallocate. ** ** Returns: ** none. ** ** Side Effects: ** housekeeping necessary to dispose of an envelope. ** Unlocks this queue file. */ dropenvelope(e) register ENVELOPE *e; { bool queueit = FALSE; register ADDRESS *q; #ifdef DEBUG if (tTd(50, 1)) { printf("dropenvelope %x id=", e); xputs(e->e_id); printf(" flags=%o\n", e->e_flags); } #endif DEBUG #ifdef LOG if (LogLevel > 10) syslog(LOG_DEBUG, "dropenvelope, id=%s, flags=%o, pid=%d", e->e_id == NULL ? "(none)" : e->e_id, e->e_flags, getpid()); #endif LOG /* we must have an id to remove disk files */ if (e->e_id == NULL) return; /* ** Extract state information from dregs of send list. */ for (q = e->e_sendqueue; q != NULL; q = q->q_next) { if (bitset(QQUEUEUP, q->q_flags)) queueit = TRUE; } /* ** Send back return receipts as requested. */ if (e->e_receiptto != NULL && bitset(EF_SENDRECEIPT, e->e_flags)) { auto ADDRESS *rlist = NULL; sendtolist(CurEnv->e_receiptto, (ADDRESS *) NULL, &rlist); (void) returntosender("Return receipt", rlist, FALSE); } /* ** Arrange to send error messages if there are fatal errors. */ if (bitset(EF_FATALERRS|EF_TIMEOUT, e->e_flags) && ErrorMode != EM_QUIET) savemail(e); /* ** Instantiate or deinstantiate the queue. */ if ((!queueit && !bitset(EF_KEEPQUEUE, e->e_flags)) || bitset(EF_CLRQUEUE, e->e_flags)) { if (e->e_dfp != NULL) (void) fclose(e->e_dfp); if (e->e_df != NULL) xunlink(e->e_df); xunlink(queuename(e, 'q')); } else if (queueit || !bitset(EF_INQUEUE, e->e_flags)) { #ifdef QUEUE queueup(e, FALSE, FALSE); #else QUEUE syserr("dropenvelope: queueup"); #endif QUEUE } /* now unlock the job */ closexscript(e); unlockqueue(e); /* make sure that this envelope is marked unused */ e->e_id = e->e_df = NULL; e->e_dfp = NULL; } /* ** CLEARENVELOPE -- clear an envelope without unlocking ** ** This is normally used by a child process to get a clean ** envelope without disturbing the parent. ** ** Parameters: ** e -- the envelope to clear. ** ** Returns: ** none. ** ** Side Effects: ** Closes files associated with the envelope. ** Marks the envelope as unallocated. */ clearenvelope(e) register ENVELOPE *e; { /* clear out any file information */ if (e->e_xfp != NULL) (void) fclose(e->e_xfp); if (e->e_dfp != NULL) (void) fclose(e->e_dfp); e->e_xfp = e->e_dfp = NULL; /* now expunge names of objects */ e->e_df = e->e_id = NULL; /* and the flags which are now meaningless */ e->e_flags = 0; } /* ** UNLOCKQUEUE -- unlock the queue entry for a specified envelope ** ** Parameters: ** e -- the envelope to unlock. ** ** Returns: ** none ** ** Side Effects: ** unlocks the queue for `e'. */ unlockqueue(e) ENVELOPE *e; { /* remove the transcript */ #ifdef DEBUG # ifdef LOG if (LogLevel > 19) syslog(LOG_DEBUG, "%s: unlock", e->e_id); # endif LOG if (!tTd(51, 4)) #endif DEBUG xunlink(queuename(e, 'x')); # ifdef QUEUE /* last but not least, remove the lock */ xunlink(queuename(e, 'l')); # endif QUEUE } /* ** INITSYS -- initialize instantiation of system ** ** In Daemon mode, this is done in the child. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Initializes the system macros, some global variables, ** etc. In particular, the current time in various ** forms is set. */ initsys() { static char cbuf[5]; /* holds hop count */ static char pbuf[10]; /* holds pid */ static char ybuf[10]; /* holds tty id */ register char *p; extern char *ttyname(); extern char *macvalue(); extern char Version[]; /* ** Give this envelope a reality. ** I.e., an id, a transcript, and a creation time. */ openxscript(CurEnv); CurEnv->e_ctime = curtime(); /* ** Set OutChannel to something useful if stdout isn't it. ** This arranges that any extra stuff the mailer produces ** gets sent back to the user on error (because it is ** tucked away in the transcript). */ if (OpMode == MD_DAEMON && QueueRun) OutChannel = CurEnv->e_xfp; /* ** Set up some basic system macros. */ /* process id */ (void) sprintf(pbuf, "%d", getpid()); define('p', pbuf, CurEnv); /* hop count */ (void) sprintf(cbuf, "%d", CurEnv->e_hopcount); define('c', cbuf, CurEnv); /* time as integer, unix time, arpa time */ settime(); /* tty name */ if (macvalue('y', CurEnv) == NULL) { p = ttyname(2); if (p != NULL) { if (rindex(p, '/') != NULL) p = rindex(p, '/') + 1; (void) strcpy(ybuf, p); define('y', ybuf, CurEnv); } } } /* ** SETTIME -- set the current time. ** ** Parameters: ** none. ** ** Returns: ** none. ** ** Side Effects: ** Sets the various time macros -- $a, $b, $d, $t. */ settime() { register char *p; auto time_t now; static char tbuf[20]; /* holds "current" time */ static char dbuf[30]; /* holds ctime(tbuf) */ register struct tm *tm; extern char *arpadate(); extern struct tm *gmtime(); extern char *macvalue(); now = curtime(); tm = gmtime(&now); (void) sprintf(tbuf, "%02d%02d%02d%02d%02d", tm->tm_year, tm->tm_mon+1, tm->tm_mday, tm->tm_hour, tm->tm_min); define('t', tbuf, CurEnv); (void) strcpy(dbuf, ctime(&now)); *index(dbuf, '\n') = '\0'; if (macvalue('d', CurEnv) == NULL) define('d', dbuf, CurEnv); p = newstr(arpadate(dbuf)); if (macvalue('a', CurEnv) == NULL) define('a', p, CurEnv); define('b', p, CurEnv); } /* ** QUEUENAME -- build a file name in the queue directory for this envelope. ** ** Assigns an id code if one does not already exist. ** This code is very careful to avoid trashing existing files ** under any circumstances. ** We first create an nf file that is only used when ** assigning an id. This file is always empty, so that ** we can never accidently truncate an lf file. ** ** Parameters: ** e -- envelope to build it in/from. ** type -- the file type, used as the first character ** of the file name. ** ** Returns: ** a pointer to the new file name (in a static buffer). ** ** Side Effects: ** Will create the lf and qf files if no id code is ** already assigned. This will cause the envelope ** to be modified. */ char * queuename(e, type) register ENVELOPE *e; char type; { static char buf[MAXNAME]; static int pid = -1; char c1 = 'A'; char c2 = 'A'; if (e->e_id == NULL) { char qf[20]; char lf[20]; char nf[20]; /* find a unique id */ if (pid != getpid()) { /* new process -- start back at "AA" */ pid = getpid(); c1 = 'A'; c2 = 'A' - 1; } (void) sprintf(qf, "qfAA%05d", pid); strcpy(lf, qf); lf[0] = 'l'; strcpy(nf, qf); nf[0] = 'n'; while (c1 < '~' || c2 < 'Z') { int i; if (c2 >= 'Z') { c1++; c2 = 'A' - 1; } qf[2] = lf[2] = nf[2] = c1; qf[3] = lf[3] = nf[3] = ++c2; # ifdef DEBUG if (tTd(7, 20)) printf("queuename: trying \"%s\"\n", nf); # endif DEBUG # ifdef QUEUE if (access(lf, 0) >= 0 || access(qf, 0) >= 0) continue; errno = 0; i = creat(nf, FileMode); if (i < 0) { (void) unlink(nf); /* kernel bug */ continue; } (void) close(i); i = link(nf, lf); (void) unlink(nf); if (i < 0) continue; if (link(lf, qf) >= 0) break; (void) unlink(lf); # else QUEUE if (close(creat(qf, FileMode)) < 0) continue; # endif QUEUE } if (c1 >= '~' && c2 >= 'Z') { syserr("queuename: Cannot create \"%s\" in \"%s\"", lf, QueueDir); exit(EX_OSERR); } e->e_id = newstr(&qf[2]); define('i', e->e_id, e); # ifdef DEBUG if (tTd(7, 1)) printf("queuename: assigned id %s, env=%x\n", e->e_id, e); # ifdef LOG if (LogLevel > 16) syslog(LOG_DEBUG, "%s: assigned id", e->e_id); # endif LOG # endif DEBUG } if (type == '\0') return (NULL); (void) sprintf(buf, "%cf%s", type, e->e_id); # ifdef DEBUG if (tTd(7, 2)) printf("queuename: %s\n", buf); # endif DEBUG return (buf); } /* ** OPENXSCRIPT -- Open transcript file ** ** Creates a transcript file for possible eventual mailing or ** sending back. ** ** Parameters: ** e -- the envelope to create the transcript in/for. ** ** Returns: ** none ** ** Side Effects: ** Creates the transcript file. */ openxscript(e) register ENVELOPE *e; { register char *p; # ifdef LOG if (LogLevel > 19) syslog(LOG_DEBUG, "%s: openx%s", e->e_id, e->e_xfp == NULL ? "" : " (no)"); # endif LOG if (e->e_xfp != NULL) return; p = queuename(e, 'x'); e->e_xfp = fopen(p, "w"); if (e->e_xfp == NULL) syserr("Can't create %s", p); else (void) chmod(p, 0644); } /* ** CLOSEXSCRIPT -- close the transcript file. ** ** Parameters: ** e -- the envelope containing the transcript to close. ** ** Returns: ** none. ** ** Side Effects: ** none. */ closexscript(e) register ENVELOPE *e; { if (e->e_xfp == NULL) return; (void) fclose(e->e_xfp); e->e_xfp = NULL; } /* ** SETSENDER -- set the person who this message is from ** ** Under certain circumstances allow the user to say who ** s/he is (using -f or -r). These are: ** 1. The user's uid is zero (root). ** 2. The user's login name is in an approved list (typically ** from a network server). ** 3. The address the user is trying to claim has a ** "!" character in it (since #2 doesn't do it for ** us if we are dialing out for UUCP). ** A better check to replace #3 would be if the ** effective uid is "UUCP" -- this would require me ** to rewrite getpwent to "grab" uucp as it went by, ** make getname more nasty, do another passwd file ** scan, or compile the UID of "UUCP" into the code, ** all of which are reprehensible. ** ** Assuming all of these fail, we figure out something ** ourselves. ** ** Parameters: ** from -- the person we would like to believe this message ** is from, as specified on the command line. ** ** Returns: ** none. ** ** Side Effects: ** sets sendmail's notion of who the from person is. */ setsender(from) char *from; { register char **pvp; register struct passwd *pw = NULL; char *realname = NULL; char buf[MAXNAME]; extern char *macvalue(); extern char **prescan(); extern bool safefile(); extern char *FullName; # ifdef DEBUG if (tTd(45, 1)) printf("setsender(%s)\n", from == NULL ? "" : from); # endif DEBUG /* ** Figure out the real user executing us. ** Username can return errno != 0 on non-errors. */ if (QueueRun || OpMode == MD_SMTP || OpMode == MD_ARPAFTP) realname = from; if (realname == NULL || realname[0] == '\0') { extern char *username(); realname = username(); errno = 0; } if (realname == NULL || realname[0] == '\0') { extern struct passwd *getpwuid(); pw = getpwuid(getruid()); if (pw != NULL) realname = pw->pw_name; } if (realname == NULL || realname[0] == '\0') { syserr("Who are you?"); realname = "root"; } /* ** Determine if this real person is allowed to alias themselves. */ if (from != NULL) { extern bool trusteduser(); if (!trusteduser(realname) && # ifdef DEBUG (!tTd(1, 9) || getuid() != geteuid()) && # endif DEBUG index(from, '!') == NULL && getuid() != 0) { /* network sends -r regardless (why why why?) */ /* syserr("%s, you cannot use the -f flag", realname); */ from = NULL; } else if (strcmp(from, realname) != 0) pw = NULL; } SuprErrs = TRUE; if (from == NULL || parseaddr(from, &CurEnv->e_from, 1, '\0') == NULL) { from = newstr(realname); (void) parseaddr(from, &CurEnv->e_from, 1, '\0'); } else FromFlag = TRUE; CurEnv->e_from.q_flags |= QDONTSEND; SuprErrs = FALSE; if (pw == NULL && CurEnv->e_from.q_mailer == LocalMailer) { extern struct passwd *getpwnam(); pw = getpwnam(CurEnv->e_from.q_user); } /* ** Process passwd file entry. */ if (pw != NULL) { /* extract home directory */ CurEnv->e_from.q_home = newstr(pw->pw_dir); /* extract user and group id */ CurEnv->e_from.q_uid = pw->pw_uid; CurEnv->e_from.q_gid = pw->pw_gid; /* run user's .mailcf file */ define('z', CurEnv->e_from.q_home, CurEnv); expand("$z/.mailcf", buf, &buf[sizeof buf - 1], CurEnv); if (safefile(buf, getruid(), S_IREAD)) readcf(buf, FALSE); /* if the user has given fullname already, don't redefine */ if (FullName == NULL) FullName = macvalue('x', CurEnv); if (FullName != NULL && FullName[0] == '\0') FullName = NULL; /* extract full name from passwd file */ if (FullName == NULL && pw->pw_gecos != NULL && strcmp(pw->pw_name, CurEnv->e_from.q_user) == 0) { buildfname(pw->pw_gecos, CurEnv->e_from.q_user, buf); if (buf[0] != '\0') FullName = newstr(buf); } if (FullName != NULL) define('x', FullName, CurEnv); } else { #ifndef V6 if (CurEnv->e_from.q_home == NULL) CurEnv->e_from.q_home = getenv("HOME"); #endif V6 CurEnv->e_from.q_uid = getuid(); CurEnv->e_from.q_gid = getgid(); } if (CurEnv->e_from.q_uid != 0) { DefUid = CurEnv->e_from.q_uid; DefGid = CurEnv->e_from.q_gid; } /* ** Rewrite the from person to dispose of possible implicit ** links in the net. */ pvp = prescan(from, '\0'); if (pvp == NULL) { syserr("cannot prescan from (%s)", from); finis(); } rewrite(pvp, 3); rewrite(pvp, 1); rewrite(pvp, 4); cataddr(pvp, buf, sizeof buf); define('f', newstr(buf), CurEnv); /* save the domain spec if this mailer wants it */ if (bitnset(M_CANONICAL, CurEnv->e_from.q_mailer->m_flags)) { extern char **copyplist(); while (*pvp != NULL && strcmp(*pvp, "@") != 0) pvp++; if (*pvp != NULL) CurEnv->e_fromdomain = copyplist(pvp, TRUE); } } /* ** TRUSTEDUSER -- tell us if this user is to be trusted. ** ** Parameters: ** user -- the user to be checked. ** ** Returns: ** TRUE if the user is in an approved list. ** FALSE otherwise. ** ** Side Effects: ** none. */ bool trusteduser(user) char *user; { register char **ulist; extern char *TrustedUsers[]; for (ulist = TrustedUsers; *ulist != NULL; ulist++) if (strcmp(*ulist, user) == 0) return (TRUE); return (FALSE); }