Bugtraq mailing list archives

Re: your mail


From: nettech () crl com (Joseph W. Stroup)
Date: Sun, 2 Oct 1994 19:33:26 -0700 (PDT)


This is got to stop now ! Everyone of these I get I will bounce to the 
SENDER!

Joseph Stroup

On Sun, 2 Oct 1994, Tim Newsham wrote:

#ifndef lint
static        char sccsid[] = "@(#)mail.c 1.1 90/10/29 SMI; from UCB 4.15 83/04/12";
#endif

/*
 * /bin/mail - local delivery
 */
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/file.h>

#include <ctype.h>
#include <stdio.h>
#include <pwd.h>
#include <utmp.h>
#include <signal.h>
#include <setjmp.h>
#include <sysexits.h>

#define SENDMAIL      "/usr/lib/sendmail"

      /* copylet flags */
#define REMOTE                1               /* remote mail, add rmtmsg */
#define ORDINARY      2
#define ZAP           3               /* zap header and trailing empty line */
#define       FORWARD         4

#define       LSIZE           256
#define       MAXLET          300             /* maximum number of letters */
#define       MAILMODE        0600            /* mode of created mail */

char  line[LSIZE];
char  resp[LSIZE];
struct let {
      long    adr;
      char    change;
} let[MAXLET];
int   nlet    = 0;
char  lfil[50];
long  iop, time();
char  *getenv();
char  *index();
char  lettmp[] = "/tmp/maXXXXX";
char  maildir[] = "/var/spool/mail/";
char  mailfile[] = "/var/spool/mail/xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx";
char  dead[] = "dead.letter";
char  forwmsg[] = " forwarded\n";
FILE  *tmpf;
FILE  *malf;
char  *my_name;
char  *getlogin();
int   error;
int   changed;
int   forward;
char  from[] = "From ";
long  ftell();
void  delex();
char  *ctime();
int   flgf;
int   flgp;
int   flge;
int   flgt;
int   delflg = 1;
int   hseqno;
jmp_buf       sjbuf;
int   rmail;

main(argc, argv)
char **argv;
{
      register i;
      struct passwd *pwent;

      mktemp(lettmp);
      unlink(lettmp);
      my_name = getlogin();
      if (my_name == NULL || *my_name == '\0') {
              pwent = getpwuid(getuid());
              if (pwent==NULL)
                      my_name = "???";
              else
                      my_name = pwent->pw_name;
      }
      else {
              pwent = getpwnam(my_name);
              if ( getuid() != pwent->pw_uid) {
                      pwent = getpwuid(getuid());
                      my_name = pwent->pw_name;
              }
      }
      if (setjmp(sjbuf))
              done();
      for (i=SIGHUP; i<=SIGTERM; i++)
              setsig(i, delex);
      tmpf = fopen(lettmp, "w+r");
      if (tmpf == NULL)
              panic("mail: %s: cannot open for writing", lettmp);
      /*
       * This protects against others reading mail from temp file and
       * if we exit, the file will be deleted already.
       */
      unlink(lettmp);
      if (argv[0][0] == 'r')
              rmail++;
      if (argv[0][0] != 'r' &&        /* no favors for rmail*/
         (argc == 1 || argv[1][0] == '-' && !any(argv[1][1], "hdt")
                      && (argv[argc-1][0] == '-' || argv[argc-2][1] == 'f')))
                                      /* -r can be an option in both
                                       * cases. If last arg is an option
                                       * or it's an argument to -f
                                       * call printmail; otherwise (it's
                                       * a user name), call bulkmail.
                                       */
              printmail(argc, argv);
      else
              bulkmail(argc, argv);
      done();
      /* NOTREACHED */
}

setsig(i, f)
int i;
void (*f)();
{
      if (signal(i, SIG_IGN) != SIG_IGN)
              signal(i, f);
}

any(c, str)
      register int c;
      register char *str;
{

      while (*str)
              if (c == *str++)
                      return(1);
      return(0);
}

printmail(argc, argv)
      char **argv;
{
      int flg, i, j, print;
      char *p, *getarg();
      struct stat statb;

      setuid(getuid());
      cat(mailfile, maildir, my_name);
      for (; argc > 1; argv++, argc--) {
              if (argv[1][0] != '-')
                      break;
              switch (argv[1][1]) {

              case 'p':
                      flgp++;
                      /* fall thru... */
              case 'q':
                      delflg = 0;
                      break;

              case 'f':
                      if (argv[1][2] == '\0') {
                              if (argc >= 3) {
                                      strcpy(mailfile, argv[2]);
                                      argv++, argc--;
                              }
                      }
                      else    
                              strcpy(mailfile, &argv[1][2]);
                      break;

              case 'r':
                      forward = 1;
                      break;

              case 'e':
                      flge++;
                      break;

              default:
                      panic("unknown option %c", argv[1][1]);
                      /*NOTREACHED*/
              }
      }
      malf = fopen(mailfile, "r");
      if (malf == NULL) {
              if (!flge) {
                      printf("No mail.\n");
                      return;
              }
              else {
                      fclose(tmpf);
                      error = 1;
                      done();
              }       
      }
      lock(mailfile);
      copymt(malf, tmpf);
      fclose(malf);
      unlock();
      
      /* if e option given, dont' need to go any further */
      if (flge) {
              fclose(tmpf);
              if (nlet)
                      done();
              else {
                      error = 1;
                      done();
              }       
      }

      fseek(tmpf, 0, L_SET);

      changed = 0;
      print = 1;
      for (i = 0; i < nlet; ) {
              j = forward ? i : nlet - i - 1;
              if (setjmp(sjbuf)) {
                      print = 0;
              } else {
                      if (print)
                              copylet(j, stdout, ORDINARY);
                      print = 1;
              }
              if (flgp) {
                      i++;
                      continue;
              }
              setjmp(sjbuf);
              printf( "? ");
              fflush(stdout);
              if (fgets(resp, LSIZE, stdin) == NULL)
                      break;
              switch (resp[0]) {

              default:
                      printf("usage\n");
              case '?':
              case '*':
                      print = 0;
                      printf("q\tquit\n");
                      printf("x\texit without changing mail\n");
                      printf("p\tprint\n");
                      printf("s[file]\tsave (default mbox)\n");
                      printf("w[file]\tsame without header\n");
                      printf("-\tprint previous\n");
                      printf("d\tdelete\n");
                      printf("+\tnext (no delete)\n");
                      printf("m user\tmail to user\n");
                      printf("! cmd\texecute cmd\n");
                      break;

              case '+':
              case 'n':
              case '\n':
                      i++;
                      break;
              case 'x':
                      changed = 0;
              case 'q':
                      goto donep;
              case 'p':
                      break;
              case '^':
              case '-':
                      if (--i < 0)
                              i = 0;
                      break;
              case 'y':
              case 'w':
              case 's':
                      flg = 0;
                      if (resp[1] != '\n' && resp[1] != ' ') {
                              printf("illegal\n");
                              flg++;
                              print = 0;
                              continue;
                      }
                      if (resp[1] == '\n' || resp[1] == '\0') {
                              p = getenv("HOME");
                              if (p != 0)
                                      cat(resp+1, p, "/mbox");
                              else
                                      cat(resp+1, "", "mbox");
                      }
                      for (p = resp+1; (p = getarg(lfil, p)) != NULL; ) {
                              malf = fopen(lfil, "a");
                              if (malf == NULL) {
                                      printf("mail: %s: cannot append\n",
                                          lfil);
                                      flg++;
                                      continue;
                              }
                              copylet(j, malf, resp[0]=='w'? ZAP: ORDINARY);
                              fclose(malf);
                      }
                      if (flg)
                              print = 0;
                      else {
                              let[j].change = 'd';
                              changed++;
                              i++;
                      }
                      break;
              case 'm':
                      flg = 0;
                      if (resp[1] == '\n' || resp[1] == '\0') {
                              i++;
                              continue;
                      }
                      if (resp[1] != ' ') {
                              printf("invalid command\n");
                              flg++;
                              print = 0;
                              continue;
                      }
                      for (p = resp+1; (p = getarg(lfil, p)) != NULL; )
                              if (!sendmail(j, lfil, my_name))
                                      /* couldn't send it */
                                      flg++;
                      if (flg)
                              print = 0;
                      else {
                              let[j].change = 'd';
                              changed++;
                              i++;
                      }
                      break;
              case '!':
                      system(resp+1);
                      printf("!\n");
                      print = 0;
                      break;
              case 'd':
                      let[j].change = 'd';
                      changed++;
                      i++;
                      if (resp[1] == 'q')
                              goto donep;
                      break;
              }
      }
   donep:
      if (changed)
              copyback();
}

/* copy temp or whatever back to /var/spool/mail */
copyback()
{
      register i, c;
      int fd, new = 0, oldmask;
      struct stat stbuf;

#define       mask(s) (1 << ((s) - 1))
      oldmask = sigblock(mask(SIGINT)|mask(SIGHUP)|mask(SIGQUIT));
#undef mask
      lock(mailfile);
      fd = open(mailfile, O_RDWR | O_CREAT, MAILMODE);
      if (fd >= 0) {
              malf = fdopen(fd, "r+w");
      }
      if (fd < 0 || malf == NULL)
              panic("can't rewrite %s", lfil);
      fstat(fd, &stbuf);
      if (stbuf.st_size != let[nlet].adr) {   /* new mail has arrived */
              fseek(malf, let[nlet].adr, L_SET);
              fseek(tmpf, let[nlet].adr, L_SET);
              while ((c = getc(malf)) != EOF)
                      putc(c, tmpf);
              let[++nlet].adr = stbuf.st_size;
              new = 1;
              fseek(malf, 0, L_SET);
      }
      ftruncate(fd, 0);
      for (i = 0; i < nlet; i++)
              if (let[i].change != 'd')
                      copylet(i, malf, ORDINARY);
      fclose(malf);
      if (new)
              printf("New mail has arrived.\n");
      unlock();
      sigsetmask(oldmask);
}

/* copy mail (f1) to temp (f2) */
copymt(f1, f2)
      FILE *f1, *f2;
{
      long nextadr;

      nlet = nextadr = 0;
      let[0].adr = 0;
      while (fgets(line, LSIZE, f1) != NULL) {
              if (isfrom(line))
                      let[nlet++].adr = nextadr;
              nextadr += strlen(line);
              fputs(line, f2);
      }
      let[nlet].adr = nextadr;        /* last plus 1 */
}

copylet(n, f, type)
      FILE *f;
{
      int ch;
      long k;
      char hostname[32];


      fseek(tmpf, let[n].adr, L_SET);
      k = let[n+1].adr - let[n].adr;
      while (k-- > 1 && (ch = getc(tmpf)) != '\n')
              if (type != ZAP)
                      putc(ch, f);
      switch (type) {

      case REMOTE:
              gethostname(hostname, sizeof (hostname));
              fprintf(f, " remote from %s\n", hostname);
              break;

      case FORWARD:
              fprintf(f, forwmsg);
              break;

      case ORDINARY:
              putc(ch, f);
              break;

      case ZAP:
              break;

      default:
              panic("Bad letter type %d to copylet.", type);
      }
      while (k-- > 1) {
              ch = getc(tmpf);
              putc(ch, f);
      }
      if (type != ZAP || ch != '\n')
              putc(getc(tmpf), f);
}

isfrom(lp)
register char *lp;
{
      register char *p;

      for (p = from; *p; )
              if (*lp++ != *p++)
                      return(0);
      return(1);
}

bulkmail(argc, argv)
char **argv;
{
      int aret;
      char **args;
      char truename[100];
      int first;
      register char *cp;
      char *newargv[1000];
      register char **ap;
      register char **vp;
      int dflag;
      int a_count=0;
      int gaver=0;

      dflag = 0;
      if (argc < 1) {
              fprintf(stderr, "puke\n");
              return;
      }
      for (vp = argv, ap = newargv + 1; (*ap = *vp++) != 0; ap++)
              if (ap[0][0] == '-' && ap[0][1] == 'd')
                      dflag++;
      if (!dflag) {
              /* give it to sendmail, rah rah! */
              unlink(lettmp);
              ap = newargv+1;
              if (rmail)
                      *ap-- = "-s";
              *ap = "-sendmail";
              setuid(getuid());
              execv(SENDMAIL, ap);
              perror(SENDMAIL);
              exit(EX_UNAVAILABLE);
      }

      truename[0] = 0;
      line[0] = '\0';

      /*
       * When we fall out of this, argv[1] should be first name,
       * argc should be number of names + 1.
       */

      while (argc > 1 && *argv[1] == '-') {
              cp = *++argv;
              argc--;
              a_count++;
              switch (cp[1]) {
              case 'r':
                      if (argc <= 1)
                              usage();
                      gaver++;
                      strcpy(truename, argv[1]);
                      fgets(line, LSIZE, stdin);
                      if (strncmp("From", line, 4) == 0)
                              line[0] = '\0';
                      argv++;
                      argc--;
                      break;
              case 'h':
                      if (argc <= 1)
                              usage();
                      hseqno = atoi(argv[1]);
                      argv++;
                      argc--;
                      break;

              case 't':
                      flgt++;
                      break;

              case 'd':
                      break;
              
              default:
                      usage();
              }
      }
      if (argc <= 1)
              usage();
      if (rmail && ((a_count>1) || (a_count==1 && !flgt)))
              usage();
      if (gaver == 0)
              strcpy(truename, my_name);

      time(&iop);
      fprintf(tmpf, "%s%s %s", from, truename, ctime(&iop));

      /* Copy to list in mail entry? */
      if (flgt && argc > 0 ) {
              aret = argc;
              args = argv;
              fprintf(tmpf,"To: ");
              while (--aret > 0)
                      fprintf(tmpf,"%s ", *++args);
              fprintf(tmpf,"\n");
      }

      iop = ftell(tmpf);
      flgf = first = 1;
      for (;;) {
              if (first) {
                      first = 0;
                      if (*line == '\0' && fgets(line, LSIZE, stdin) == NULL)
                              break;
              } else {
                      if (fgets(line, LSIZE, stdin) == NULL)
                              break;
              }
              if (*line == '.' && line[1] == '\n' && isatty(fileno(stdin)))
                      break;
              if (isfrom(line))
                      putc('>', tmpf);
              fputs(line, tmpf);
              flgf = 0;
      }
      putc('\n', tmpf);
      nlet = 1;
      let[0].adr = 0;
      let[1].adr = ftell(tmpf);
      if (flgf)
              return;
      while (--argc > 0)
              if (!sendmail(0, *++argv, truename))
                      error++;
      if (error) {
              /* Don't return count of errors, return a defined code. */
              error = EX_UNAVAILABLE;

              /* Also, try to save dead.letter */
              if (safefile(dead)) {
                      setuid(getuid());
                      malf = fopen(dead, "w");
                      if (malf == NULL) {
                              printf( "mail: cannot open %s\n", dead);
                              fclose(tmpf);
                              return;
                      }
                      copylet(0, malf, ZAP);
                      fclose(malf);
                      printf( "Mail saved in %s\n", dead);
              }
      }
      fclose(tmpf);
}

sendrmt(n, name)
char *name;
{
      FILE *rmf, *popen();
      register char *p;
      char rsys[64], cmd[64];
      register pid;
      int sts;

      for (p=rsys; *name!='!'; *p++ = *name++)
              if (*name=='\0')
                      return(0);      /* local address, no '!' */
      *p = '\0';
      if (name[1]=='\0') {
              printf("null name\n");
              return(0);
      }
skip:
      if ((pid = fork()) == -1) {
              fprintf(stderr, "mail: can't create proc for remote\n");
              return(0);
      }
      if (pid) {
              while (wait(&sts) != pid) {
                      if (wait(&sts)==-1)
                              return(0);
              }
              return(!sts);
      }
      setuid(getuid());
      if (any('!', name+1))
              sprintf(cmd, "uux - %s!rmail \\(%s\\)", rsys, name+1);
      else
              sprintf(cmd, "uux - %s!rmail %s", rsys, name+1);
      if ((rmf=popen(cmd, "w")) == NULL)
              exit(1);
      copylet(n, rmf, REMOTE);
      exit(pclose(rmf) != 0);
}

usage()
{

      fprintf(stderr, "Usage: mail [-r] [-t] [-p] [-q] [-e] [-h seqno] [-f fname] [people] . . .\n");
      error = EX_USAGE;
      done();
}

#include <sys/socket.h>
#include <netinet/in.h>

notifybiff(msg)
      char *msg;
{
      static struct sockaddr_in addr;
      static int f = -1;

      if (addr.sin_family == 0) {
              addr.sin_family = AF_INET;
              addr.sin_addr.s_addr = INADDR_ANY;
              addr.sin_port = htons(IPPORT_BIFFUDP);
      }
      if (f < 0)
              f = socket(AF_INET, SOCK_DGRAM, 0);
      sendto(f, msg, strlen(msg)+1, 0, &addr, sizeof (addr));
}

sendmail(n, name, fromaddr)
      int n;
      char *name;
      char *fromaddr;
{
      char file[256];
      int fd;
      struct passwd *pw;
      char buf[128];
      int realuser;

      if (*name=='!')
              name++;
      if (any('!', name))
              return (sendrmt(n, name));
      if ((pw = getpwnam(name)) == NULL) {
              printf("mail: can't send to %s\n", name);
              return(0);
      }
      cat(file, maildir, name);
      if (!safefile(file))
              return(0);
        /*
         * Remember the real UID, and temporarily become the target
         * user in case we are going across NFS
         */
      realuser = getuid();
      setreuid(0,pw->pw_uid);
      lock(file);
      fd = open(file, O_WRONLY | O_CREAT, MAILMODE);
      if (fd >= 0) {
              flock(fd, LOCK_EX);
              malf = fdopen(fd, "a");
      }
      if (fd < 0 || malf == NULL) {
              unlock();
              close(fd);
              printf("mail: %s: cannot append\n", file);
              setuid(0);
              setreuid(realuser,0);
              return(0);
      }
      setuid(0);
      setreuid(realuser,0);
      fchown(fd, pw->pw_uid, pw->pw_gid);
      sprintf(buf, "%s@%d\n", name, ftell(malf)); 
      copylet(n, malf, ORDINARY);
      fclose(malf);
      setreuid(0,pw->pw_uid);
      unlock();
      notifybiff(buf);
      setuid(0);
      setreuid(realuser,0);
      return(1);
}

void
delex(i)
{
      setsig(i, delex);
      putc('\n', stderr);
      if (delflg)
              longjmp(sjbuf, 1);
      done();
}

/*
 * Lock the specified mail file by setting the file mailfile.lock.
 * We must, of course, be careful to unlink the lock file by a call
 * to unlock before we stop.  The algorithm used here is to see if
 * the lock exists, and if it does, to check its modify time.  If it
 * is older than 300 seconds, we assume error and set our own file.
 * Otherwise, we wait for 5 seconds and try again.
 */

char  *maillock       = ".lock";              /* Lock suffix for mailname */
char  *lockname       = "/var/spool/mail/tmXXXXXX";
char  locktmp[30];                            /* Usable lock temporary */
char  curlock[50];                            /* Last used name of lock */
int   locked;                                 /* To note that we locked it */

lock(file)
char *file;
{
      register time_t t;
      struct stat sbuf;
      int statfailed;

      if (locked || flgf)
              return(0);
      strcpy(curlock, file);
      strcat(curlock, maillock);
      strcpy(locktmp, lockname);
      mktemp(locktmp);
      unlink(locktmp);
      statfailed = 0;
      for (;;) {
              t = lock1(locktmp, curlock);
              if (t == 0) {
                      locked = 1;
                      return(0);
              }
              if (stat(curlock, &sbuf) < 0) {
                      if (statfailed++ > 5)
                              return(-1);
                      sleep(5);
                      continue;
              }
              statfailed = 0;

              /*
               * Compare the time of the temp file with the time
               * of the lock file, rather than with the current
               * time of day, since the files may reside on
               * another machine whose time of day differs from
               * ours.  If the lock file is less than 5 minutes
               * old, keep trying.
               */
              if (t < sbuf.st_ctime + 300) {
                      sleep(5);
                      continue;
              }
              unlink(curlock);
      }
}

/*
 * Remove the mail lock, and note that we no longer
 * have it locked.
 */

unlock()
{

      unlink(curlock);
      locked = 0;
}

/*
 * Attempt to set the lock by creating the temporary file,
 * then doing a link/unlink.  If it succeeds, return 0,
 * else return a guess of the current time on the machine
 * holding the file.
 */

lock1(tempfile, name)
      char tempfile[], name[];
{
      register int fd;
      struct stat sbuf;

      fd = creat(tempfile, 0);
      if (fd < 0)
              return(time(0));
      fstat(fd, &sbuf);
      close(fd);
      if (link(tempfile, name) < 0) {
              unlink(tempfile);
              return(sbuf.st_ctime);
      }
      unlink(tempfile);
      return(0);
}

done()
{
      if(locked)
              unlock();
      unlink(lettmp);
      unlink(locktmp);
      exit(error);
}

cat(to, from1, from2)
      char *to, *from1, *from2;
{
      register char *cp, *dp;

      cp = to;
      for (dp = from1; *cp = *dp++; cp++)
              ;
      for (dp = from2; *cp++ = *dp++; )
              ;
}

/* copy p... into s, update p */
char *
getarg(s, p)
      register char *s, *p;
{
      while (*p == ' ' || *p == '\t')
              p++;
      if (*p == '\n' || *p == '\0')
              return(NULL);
      while (*p != ' ' && *p != '\t' && *p != '\n' && *p != '\0')
              *s++ = *p++;
      *s = '\0';
      return(p);
}

safefile(f)
      char *f;
{
      struct stat statb;

      if (lstat(f, &statb) < 0)
              return (1);
      if (statb.st_nlink != 1 || (statb.st_mode & S_IFMT) == S_IFLNK) {
              fprintf(stderr,
                  "mail: %s has more than one link or is a symbolic link\n",
                  f);
              return (0);
      }
      return (1);
}

panic(msg, a1, a2, a3)
      char *msg;
{

      fprintf(stderr, "mail: ");
      fprintf(stderr, msg, a1, a2, a3);
      fprintf(stderr, "\n");
      error = EX_OSERR;
      done();
}




Current thread: