home | help | back | first | fref | pref | prev | next | nref | lref | last | post |
Received: from PACIFIC-CARRIER-ANNEX.MIT.EDU by po6.MIT.EDU (5.61/4.7) id AA19648; Mon, 3 Oct 94 01:45:55 EDT Received: from villa.fc.net by MIT.EDU with SMTP id AA01001; Mon, 3 Oct 94 01:45:53 EDT Received: from freeside.fc.net (freeside.fc.net [198.6.198.2]) by villa.fc.net (8.6.8.1/8.6.6) with ESMTP id PAA00231 for <bugtraq-outgoing@villa.fc.net>; Sun, 2 Oct 1994 15:44:53 -0500 Received: (from majordom@localhost) by freeside.fc.net (8.6.8.1/8.6.6) id PAA22387 for bugtraq-outgoing@villa.fc.net; Sun, 2 Oct 1994 15:45:21 -0500 Received: from crimelab.crimelab.com (crimelab.crimelab.com [198.64.127.1]) by freeside.fc.net (8.6.8.1/8.6.6) with ESMTP id PAA22321 for <bugtraq@fc.net>; Sun, 2 Oct 1994 15:44:55 -0500 Received: from relay1.Hawaii.Edu (relay1.Hawaii.Edu [128.171.41.53]) by crimelab.crimelab.com (8.6.9/8.6.4) with SMTP id PAA02007 for <bugtraq@crimelab.com>; Sun, 2 Oct 1994 15:40:19 -0500 Received: from uhunix.uhcc.Hawaii.Edu ([128.171.44.6]) by relay1.Hawaii.Edu with SMTP id <11346>; Sun, 2 Oct 1994 08:07:57 -1000 Received: by uhunix.uhcc.Hawaii.Edu id <184397>; Sun, 2 Oct 1994 08:07:52 -1000 Message-Id: <94Oct2.080752hst.184397@uhunix.uhcc.Hawaii.Edu> From: Tim Newsham <newsham@uhunix.uhcc.hawaii.edu> To: bugtraq@crimelab.com Date: Sun, 2 Oct 1994 08:07:39 -1000 Sender: bugtraq-owner@crimelab.com Precedence: bulk #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(); }
home | help | back | first | fref | pref | prev | next | nref | lref | last | post |