Solved

Configuring sendmail to archive mail

Posted on 2000-03-13
2
329 Views
Last Modified: 2013-12-15
I'd like to have our mail server keep an archive copy of all email traffic.

I've got incomming mail covered (sending a copy to a dummy account using /etc/aliases), but I'm not sure how to keep a copy of outgoing mail.
0
Comment
Question by:sprinkmeier
2 Comments
 
LVL 40

Accepted Solution

by:
jlevie earned 200 total points
Comment Utility
There are two ways you can accomplish that.

The "-X some-log-file" argument to sendmail is the cheap and dirty way. The disadvantage is that it logs everything, the raw sendmail interactions, local delivery, the works. The data is there, but the format isn't very usable as as a message may be split up in the log file, interspersed with parts of other messages (it's a real time log).

The second and better way is to use Axel Reinhold's sendmail logging mod. It simply logs, in normal unix mailbox format, each message received by sendmail, both from external mailservers and from internal clients. The only disadvantage to it is that you do have to incorporate it into the the sendmail source (get it the 8.9.3 sources from www.sendmail.org) and compile your own sendmail, but it works really, really well. (I can probably help with compiling sendmail also)

On the chance that you are willing to use the latter method, I've put the mod below. Instructions for adding it to sendmail and using it are in the comments at the beginning. Paste it into a file named "logall.c" and follow its instructions.

---snip, snip---begin logall.c---
/* ------------------------------------------------------------
 *      "LogAll" feature to log any message through this MTA
 *      (c) /ARX cleanware 1998
 */
{
#define LOGALLHEADER      "X-Logged"
#define LOGALLLEVEL      12

      char logallrcsid[] =
            "$Id: logall.c,v 1.11 1998/09/19 10:12:04 root Exp root $";
/*
 *      $Log: logall.c,v $
 *      Revision 1.11  1998/09/19 10:12:04  root
 *      published
 *
 *      Revision 1.7  1998/09/17 07:12:49  root
 *      clobbered qtimestr
 *
 *      ABSTRACT
 *      This routine logs every message in a mail folder
 *      before it is actually transmitted. The log is
 *      "envelope-oriented" which means it logs every message
 *      once - not for every recipient. You must also see
 *      the sendmail syslog for actual delivery. The log
 *      includes full message bodys.
 *
 *      IMPLEMENTATION
 *      The logging code is compiled into the sendmail binary
 *      with the documented and often overlooked checkcompat()
 *      routine:
 *      see "Bryan Costales & Eric Allman: sendmail 2nd Edition
 *            §20 `The checkcompat() Cookbook' p.285ff"
 *      To check whether a message was already logged it adds
 *      a new header "X-Logged" to the message with the logging
 *      host, the envelope id and queued date/time as values.
 *      As long as this info doesn't change the message is no
 *      more logged.
 *
 *      CONFIGURATION
 *      The logfile is configuerd in a new macro:
 *      D{LogAll}path
 *      where path is the full pathname of the logfile, which
 *      should be mode 600. REMEMBER THIS IS A FULL BODY LOG.
 *      So be prepared for a huge file on site with much traffic.
 *      The file must exist and every mail is appended to it.
 *      The macro should be inserted into a m4-config-file for
 *      example like this:
 *
 *            LOCAL_CONFIG
 *            D{LogAll}/var/log/mail.log
 *
 *      INSTALLATION
 *      This source fragment must be included into the source-file:
 *      .../sendmail-8.x.y/src/conf.c
 *      at the following position (with the full path name of its
 *      current location - here "/root/adm/mail/sendmail/logall.c"):
 *
 *>>old            if (tTd(49, 1))
 *>>old                  printf("checkcompat(to=%s, from=%s)\n",            
 *>>old                        to->q_paddr, e->e_from.q_paddr);          
 *>>old
 *>>new            #include "/root/adm/mail/sendmail/logall.c"
 *>>old
 *>>old            # ifdef EXAMPLE_CODE
 *
 *      the sendmail binary must be remaked and reinstalled at
 *      its proper position (normally /usr/sbin/sendmail).
 *
 *      MANAGEMENT
 *      Since the logfile produced by LogAll is a standard Unix-mailfolder,
 *      the logged mails can be managed with every Unix mail-reader or MUA.
 *      The mails inside the log can be deleted, remailed, read with any
 *      mail user agent. If the logfile is the standard mail folder of a
 *      special "LogAll-user" this is especially easy and even "mail" can
 *      or a Windows-based IMAP-client can manage the logfile.
 *
 *      COMPATIBILITY
 *      LogAll is tested with sendmail-8.8.5 and sendmail-8.9.1
 *      under Linux 2.0.29.
 *
 *      AUTHOR
 *      Axel Reinhold - arx@hof.baynet.de
 *
 *      LICENSE/WARRANTY
 *      The software is provided "AS IS" without warranties of any kind,
 *      either expressed or implied, including, but not limited to the
 *      implied warranties of merchantability and fitness for a particular
 *      purpose. The entire risc of the software is with you. In no event
 *      we will be liable for any damages, including any lost profits,
 *      lost savings or other incidental damages arising out of the use
 *      or inability to use the software, even if we have been advised
 *      of the possibility of such damages, or for any claim by another party.
 *   ------------------------------------------------------------
 */

      int mid;                  /* sendmail macro id                */
      char *logall = NULL;            /* log file path from macro LogAll  */
      char bfpath[512];            /* body file path                */
      char *hlogged = LOGALLHEADER;      /* header field                      */
      char hlogval[MAXLINE];            /* header value                      */
      char *hlogvalp;                  /* header value pointer                */
      char qtimestr[80];            /* queued time string from asctime  */
      FILE *lf, *bf;                  /* log and body files                */
      ADDRESS *a;                  /* address structure                */
      HDR *h;                        /* header structure                */
      int toflag = 0;                  /* to header flag                */
      int fromflag = 0;            /* from header flag                */
      long bfpos;                  /* remember file pos                */
      size_t b_read;                  /* bytes read counter                */
      unsigned char b_buf[512];      /* buffer for read/write body          */

      mid = macid("{LogAll}", NULL);      /* get our config macro                */
      logall = macvalue(mid, e);      /* to check whether and where to log*/

      /* Use logging if macro is set and we can open the logfile          */
      if ((logall!=NULL) && ((lf = fopen(logall, "a"))!=NULL)) {
          if (tTd(49, 1))            /* debug use of logging                */
            printf("checkcompat: LogAll=%s e_id=%s\n", logall, e->e_id);

          /* construct the "X-Logged" header field */
          strcpy(qtimestr, asctime(localtime(&e->e_ctime))); /* get time  */
          if (qtimestr[strlen(qtimestr)-1]=='\n')      /* remove trailing  */
                qtimestr[strlen(qtimestr)-1]='\0';      /* line-feed asctime*/
          sprintf(hlogval, "Logged by %s as %s at %s",
            MyHostName, e->e_id, qtimestr);            /* our header field */

          /* add the header if it doesn't already exist                */
          hlogvalp = hvalue(hlogged, e->e_header);      /* get actual field */
          if (hlogvalp==NULL) addheader(hlogged, hlogval, &e->e_header);

          /* Log the message if our header didn't exist or was not from us*/
          if ((hlogvalp==NULL) || (strcmp(hlogvalp, hlogval)!=0)) {

            /* lock the logfile exclusive                            */
            if (lockfile(fileno(lf), logall, NULL, LOCK_EX)) ;
            fseek(lf, 0, SEEK_END);      /* go the eof for appending message */

            /* syslog the usage of LogAll                            */
            if (LogLevel >= LOGALLLEVEL) sm_syslog(LOG_INFO, e->e_id,
                  "LogAll to %s at %s", logall, qtimestr);

            /* print the Unix From line to separate messages in log file*/
            fprintf(lf, "From %s %s\n", e->e_from.q_user, qtimestr);

            /* scan all headers                                  */
            for (h = e->e_header; h != NULL; h = h->h_link) {

                  /* check if our header is from us and recent          */
                  /* if not change the field value to our field          */
                  if (strcasecmp(h->h_field, hlogged)==0) {
                        if (hlogvalp!=NULL) {
                              h->h_value =
                               realloc(h->h_value, strlen(hlogval)+1);
                              strcpy(h->h_value, hlogval);
                              }
                        h->h_flags &= ~H_DEFAULT;      /* set flag */
                        }

                  /* log the header if appropiate                      */
                  if ((h->h_value!=NULL) && ((h->h_flags & H_RESENT)==0)) {
                        fprintf(lf, "%s: %s\n", h->h_field, h->h_value);
                        }

                  if (h->h_flags & H_RCPT) toflag = 1;      /* to-header*/

                  }
            if (!toflag) {      /* if no to-header construct from envelope  */
                  fprintf(lf, "To:");
                  for (a = e->e_sendqueue; a != NULL; a = a->q_next)
                        fprintf(lf, " %s", a->q_paddr);
                  fprintf(lf, "\n");
                  }
            fprintf(lf, "\n");      /* separate headers from body          */

            /* now add the body from temp file or queued data file          */
            sprintf(bfpath, "%s/df%s", QueueDir, e->e_id); /* body file */
            if (e->e_dfp != NULL) {      /* if body temp file is open use it */
                  bfpos = ftell(e->e_dfp);/* remember old position    */
                  rewind(e->e_dfp);      /* read from beginning          */
                  while ((b_read = fread(b_buf, 1L, sizeof(b_buf), e->e_dfp)) != 0)
                        fwrite(b_buf, 1L, b_read, lf);
                  fseek(e->e_dfp, bfpos, SEEK_SET); /* set old pos    */
                  }
            else if ((bf = fopen(bfpath, "r")) != NULL) { /* body file  */
                  while ((b_read = fread(b_buf, 1, sizeof(b_buf), bf)) != 0)
                        fwrite(b_buf, 1, b_read, lf);
                  fclose(bf);
                  }
            else      { /* no body could be opened                      */
                  if (tTd(49, 1))
                      printf("checkcompat: fopen(%s) failed\n", bfpath);
                  }
            fprintf(lf, "\n");

            if (lockfile(fileno(lf), logall, NULL, LOCK_UN)) ; /* unlock*/
            }
          fclose(lf);
          }

/*   ------------------------------------------------------------
 *      LogAll End
 *   ------------------------------------------------------------
 */
}
0
 
LVL 2

Author Comment

by:sprinkmeier
Comment Utility
Thanks, worked exactly as required!
0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

In this tutorial I will explain how to make squid prevent malwares in five easy steps: Squid is a caching proxy for the Web supporting HTTP, HTTPS, FTP, and more. It reduces bandwidth and improves response times by caching and reusing frequently-…
How many times have you wanted to quickly do the same thing to a list but found yourself typing it again and again? I first figured out a small time saver with the up arrow to recall the last command but that can only get you so far if you have a bi…
Learn how to get help with Linux/Unix bash shell commands. Use help to read help documents for built in bash shell commands.: Use man to interface with the online reference manuals for shell commands.: Use man to search man pages for unknown command…
Get a first impression of how PRTG looks and learn how it works.   This video is a short introduction to PRTG, as an initial overview or as a quick start for new PRTG users.

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now