[BACK]Return to mail.local.c CVS log [TXT][DIR] Up to [cvs.NetBSD.org] / src / libexec / mail.local

Annotation of src/libexec/mail.local/mail.local.c, Revision 1.26.10.2

1.26.10.2! martin      1: /*     $NetBSD: mail.local.c,v 1.26.10.1 2016/07/19 14:23:10 martin Exp $      */
1.10      mrg         2:
1.1       cgd         3: /*-
1.11      mrg         4:  * Copyright (c) 1990, 1993, 1994
                      5:  *     The Regents of the University of California.  All rights reserved.
1.1       cgd         6:  *
                      7:  * Redistribution and use in source and binary forms, with or without
                      8:  * modification, are permitted provided that the following conditions
                      9:  * are met:
                     10:  * 1. Redistributions of source code must retain the above copyright
                     11:  *    notice, this list of conditions and the following disclaimer.
                     12:  * 2. Redistributions in binary form must reproduce the above copyright
                     13:  *    notice, this list of conditions and the following disclaimer in the
                     14:  *    documentation and/or other materials provided with the distribution.
1.22      agc        15:  * 3. Neither the name of the University nor the names of its contributors
1.1       cgd        16:  *    may be used to endorse or promote products derived from this software
                     17:  *    without specific prior written permission.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     20:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     21:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     22:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     23:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     24:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     25:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     26:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     27:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     28:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     29:  * SUCH DAMAGE.
                     30:  */
                     31:
1.11      mrg        32: #include <sys/cdefs.h>
1.1       cgd        33: #ifndef lint
1.25      lukem      34: __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\
                     35:  The Regents of the University of California.  All rights reserved.");
1.11      mrg        36: #if 0
                     37: static char sccsid[] = "@(#)mail.local.c       8.22 (Berkeley) 6/21/95";
                     38: #else
1.26.10.2! martin     39: __RCSID("$NetBSD: mail.local.c,v 1.26.10.1 2016/07/19 14:23:10 martin Exp $");
1.11      mrg        40: #endif
1.1       cgd        41: #endif /* not lint */
                     42:
                     43: #include <sys/param.h>
                     44: #include <sys/stat.h>
                     45: #include <sys/socket.h>
1.11      mrg        46:
1.1       cgd        47: #include <netinet/in.h>
1.11      mrg        48:
                     49: #include <errno.h>
1.1       cgd        50: #include <fcntl.h>
1.11      mrg        51: #include <pwd.h>
1.1       cgd        52: #include <netdb.h>
1.20      wiz        53: #include <stdarg.h>
1.1       cgd        54: #include <stdio.h>
                     55: #include <stdlib.h>
                     56: #include <string.h>
1.11      mrg        57: #include <syslog.h>
                     58: #include <time.h>
                     59: #include <unistd.h>
1.24      manu       60: #include <sysexits.h>
                     61:
1.11      mrg        62:
1.1       cgd        63: #include "pathnames.h"
                     64:
1.26      joerg      65: static int     deliver(int, char *, int);
                     66: __dead static void     logerr(int, const char *, ...) __printflike(2, 3);
                     67: static void    logwarn(const char *, ...) __printflike(1, 2);
                     68: static void    notifybiff(char *);
                     69: static int     store(const char *);
                     70: __dead static void     usage(void);
1.1       cgd        71:
1.11      mrg        72: int
1.26      joerg      73: main(int argc, char *argv[])
1.1       cgd        74: {
                     75:        struct passwd *pw;
1.11      mrg        76:        int ch, fd, eval, lockfile = 0;
1.1       cgd        77:        uid_t uid;
1.15      mycroft    78:        const char *from;
1.1       cgd        79:
1.11      mrg        80:        /* use a reasonable umask */
                     81:        (void) umask(0077);
                     82:
1.1       cgd        83:        openlog("mail.local", LOG_PERROR, LOG_MAIL);
                     84:
                     85:        from = NULL;
1.13      enami      86:        while ((ch = getopt(argc, argv, "ldf:r:")) != -1)
1.14      enami      87:                switch (ch) {
1.1       cgd        88:                case 'd':               /* backward compatible */
                     89:                        break;
                     90:                case 'f':
                     91:                case 'r':               /* backward compatible */
                     92:                        if (from)
1.24      manu       93:                                logerr(EX_USAGE, "multiple -f options");
1.1       cgd        94:                        from = optarg;
                     95:                        break;
1.2       deraadt    96:                case 'l':
                     97:                        lockfile++;
                     98:                        break;
1.1       cgd        99:                case '?':
                    100:                default:
                    101:                        usage();
                    102:                }
                    103:        argc -= optind;
                    104:        argv += optind;
                    105:
                    106:        if (!*argv)
                    107:                usage();
                    108:
                    109:        /*
                    110:         * If from not specified, use the name from getlogin() if the
                    111:         * uid matches, otherwise, use the name from the password file
                    112:         * corresponding to the uid.
                    113:         */
                    114:        uid = getuid();
                    115:        if (!from && (!(from = getlogin()) ||
                    116:            !(pw = getpwnam(from)) || pw->pw_uid != uid))
                    117:                from = (pw = getpwuid(uid)) ? pw->pw_name : "???";
                    118:
                    119:        fd = store(from);
1.24      manu      120:        for (eval = EX_OK; *argv; ++argv) {
                    121:                int rval;
                    122:
                    123:                rval = deliver(fd, *argv, lockfile);
                    124:                if (eval == EX_OK && rval != EX_OK)
                    125:                        eval = rval;
                    126:        }
1.10      mrg       127:        exit (eval);
1.1       cgd       128: }
                    129:
1.26      joerg     130: static int
                    131: store(const char *from)
1.1       cgd       132: {
1.11      mrg       133:        FILE *fp = NULL;        /* XXX gcc */
1.1       cgd       134:        time_t tval;
                    135:        int fd, eline;
                    136:        char *tn, line[2048];
                    137:
                    138:        tn = strdup(_PATH_LOCTMP);
1.21      itojun    139:        if (!tn)
1.24      manu      140:                logerr(EX_OSERR, "not enough core");
1.1       cgd       141:        if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+")))
1.24      manu      142:                logerr(EX_OSERR, "unable to open temporary file");
1.1       cgd       143:        (void)unlink(tn);
                    144:        free(tn);
                    145:
                    146:        (void)time(&tval);
                    147:        (void)fprintf(fp, "From %s %s", from, ctime(&tval));
                    148:
                    149:        line[0] = '\0';
                    150:        for (eline = 1; fgets(line, sizeof(line), stdin);) {
                    151:                if (line[0] == '\n')
                    152:                        eline = 1;
                    153:                else {
1.16      perry     154:                        if (eline && line[0] == 'F' && !memcmp(line, "From ", 5))
1.1       cgd       155:                                (void)putc('>', fp);
                    156:                        eline = 0;
                    157:                }
                    158:                (void)fprintf(fp, "%s", line);
                    159:                if (ferror(fp))
                    160:                        break;
                    161:        }
                    162:
                    163:        /* If message not newline terminated, need an extra. */
                    164:        if (!index(line, '\n'))
                    165:                (void)putc('\n', fp);
                    166:        /* Output a newline; note, empty messages are allowed. */
                    167:        (void)putc('\n', fp);
                    168:
                    169:        (void)fflush(fp);
                    170:        if (ferror(fp))
1.24      manu      171:                logerr(EX_OSERR, "temporary file write error");
1.26.10.1  martin    172:        if ((fd = dup(fd)) == -1)
                    173:                logerr(EX_OSERR, "dup failed");
1.23      christos  174:        (void)fclose(fp);
1.1       cgd       175:        return(fd);
                    176: }
                    177:
1.26      joerg     178: static int
                    179: deliver(int fd, char *name, int lockfile)
1.1       cgd       180: {
1.26.10.1  martin    181:        struct stat sb, nsb;
1.24      manu      182:        struct passwd pwres, *pw;
                    183:        char pwbuf[1024];
1.26.10.1  martin    184:        int created = 0, mbfd, nr, nw, off, rval=EX_OK, lfd = -1;
1.2       deraadt   185:        char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN];
1.6       pk        186:        off_t curoff;
1.1       cgd       187:
                    188:        /*
                    189:         * Disallow delivery to unknown names -- special mailboxes can be
                    190:         * handled in the sendmail aliases file.
                    191:         */
1.24      manu      192:        if ((getpwnam_r(name, &pwres, pwbuf, sizeof(pwbuf), &pw)) != 0) {
                    193:                logwarn("unable to find user %s: %s", name, strerror(errno));
                    194:                return(EX_TEMPFAIL);
                    195:        }
                    196:        if (pw == NULL) {
                    197:                logwarn("unknown name: %s", name);
                    198:                return(EX_NOUSER);
1.1       cgd       199:        }
                    200:
1.10      mrg       201:        (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name);
1.1       cgd       202:
1.10      mrg       203:        if (lockfile) {
                    204:                (void)snprintf(lpath, sizeof lpath, "%s/%s.lock",
                    205:                    _PATH_MAILDIR, name);
1.2       deraadt   206:
                    207:                if((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL,
                    208:                    S_IRUSR|S_IWUSR)) < 0) {
1.24      manu      209:                        logwarn("%s: %s", lpath, strerror(errno));
                    210:                        return(EX_OSERR);
1.2       deraadt   211:                }
                    212:        }
                    213:
1.26.10.1  martin    214:        if ((lstat(path, &sb) != -1) &&
1.1       cgd       215:            (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) {
1.24      manu      216:                logwarn("%s: linked file", path);
                    217:                return(EX_OSERR);
1.1       cgd       218:        }
1.24      manu      219:
1.10      mrg       220:        if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK,
1.26.10.2! martin    221:            S_IRUSR|S_IWUSR)) == -1) {
1.26.10.1  martin    222:                /* create file */
1.2       deraadt   223:                if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK,
1.26.10.2! martin    224:                    S_IRUSR|S_IWUSR)) == -1) {
1.24      manu      225:                        logwarn("%s: %s", path, strerror(errno));
1.26.10.1  martin    226:                        rval = EX_OSERR;
                    227:                        goto bad;
                    228:                }
                    229:                created = 1;
                    230:        } else {
                    231:                /* opened existing file, check for TOCTTOU */
                    232:                if (fstat(mbfd, &nsb) == -1) {
                    233:                        rval = EX_OSERR;
                    234:                        goto bad;
                    235:                }
                    236:
                    237:                /* file is not what we expected */
                    238:                if (nsb.st_ino != sb.st_ino || nsb.st_dev != sb.st_dev) {
                    239:                        rval = EX_OSERR;
                    240:                        goto bad;
1.10      mrg       241:                }
1.1       cgd       242:        }
                    243:
1.26.10.1  martin    244:        if ((curoff = lseek(mbfd, 0, SEEK_END)) == (off_t)-1) {
                    245:                logwarn("%s: %s", path, strerror(errno));
                    246:                rval = EX_OSERR;
                    247:                goto bad;
                    248:        }
                    249:
1.18      lukem     250:        (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name,
1.12      mrg       251:            (long long)curoff);
1.6       pk        252:        if (lseek(fd, 0, SEEK_SET) == (off_t)-1) {
1.24      manu      253:                logwarn("temporary file: %s", strerror(errno));
                    254:                rval = EX_OSERR;
1.1       cgd       255:                goto bad;
                    256:        }
                    257:
                    258:        while ((nr = read(fd, buf, sizeof(buf))) > 0)
1.3       mycroft   259:                for (off = 0; off < nr;  off += nw)
                    260:                        if ((nw = write(mbfd, buf + off, nr - off)) < 0) {
1.24      manu      261:                                logwarn("%s: %s", path, strerror(errno));
1.1       cgd       262:                                goto trunc;
                    263:                        }
                    264:        if (nr < 0) {
1.24      manu      265:                logwarn("temporary file: %s", strerror(errno));
1.1       cgd       266: trunc:         (void)ftruncate(mbfd, curoff);
1.24      manu      267:                rval = EX_OSERR;
1.1       cgd       268:        }
                    269:
                    270:        /*
                    271:         * Set the owner and group.  Historically, binmail repeated this at
                    272:         * each mail delivery.  We no longer do this, assuming that if the
                    273:         * ownership or permissions were changed there was a reason for doing
                    274:         * so.
                    275:         */
1.2       deraadt   276: bad:
1.10      mrg       277:        if (lockfile) {
                    278:                if (lfd >= 0) {
1.2       deraadt   279:                        unlink(lpath);
                    280:                        close(lfd);
                    281:                }
                    282:        }
1.1       cgd       283:
1.26.10.1  martin    284:        if (mbfd >= 0) {
                    285:                if (created)
                    286:                        (void)fchown(mbfd, pw->pw_uid, pw->pw_gid);
                    287:
                    288:                (void)fsync(mbfd);              /* Don't wait for update. */
                    289:                (void)close(mbfd);              /* Implicit unlock. */
                    290:        }
1.1       cgd       291:
1.24      manu      292:        if (rval == EX_OK)
1.1       cgd       293:                notifybiff(biffmsg);
1.24      manu      294:
                    295:        return rval;
1.1       cgd       296: }
                    297:
                    298: void
1.26      joerg     299: notifybiff(char *msg)
1.1       cgd       300: {
                    301:        static struct sockaddr_in addr;
                    302:        static int f = -1;
                    303:        struct hostent *hp;
                    304:        struct servent *sp;
                    305:        int len;
                    306:
                    307:        if (!addr.sin_family) {
                    308:                /* Be silent if biff service not available. */
                    309:                if (!(sp = getservbyname("biff", "udp")))
                    310:                        return;
                    311:                if (!(hp = gethostbyname("localhost"))) {
1.24      manu      312:                        logwarn("localhost: %s", strerror(errno));
1.1       cgd       313:                        return;
                    314:                }
1.9       mycroft   315:                addr.sin_len = sizeof(struct sockaddr_in);
1.1       cgd       316:                addr.sin_family = hp->h_addrtype;
1.9       mycroft   317:                addr.sin_port = sp->s_port;
1.16      perry     318:                memcpy(&addr.sin_addr, hp->h_addr, hp->h_length);
1.1       cgd       319:        }
                    320:        if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) {
1.24      manu      321:                logwarn("socket: %s", strerror(errno));
1.1       cgd       322:                return;
                    323:        }
                    324:        len = strlen(msg) + 1;
                    325:        if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr))
                    326:            != len)
1.24      manu      327:                logwarn("sendto biff: %s", strerror(errno));
1.1       cgd       328: }
                    329:
1.26      joerg     330: static void
                    331: usage(void)
1.1       cgd       332: {
1.24      manu      333:        logerr(EX_USAGE, "usage: mail.local [-l] [-f from] user ...");
                    334: }
                    335:
1.26      joerg     336: static void
1.24      manu      337: logerr(int status, const char *fmt, ...)
                    338: {
                    339:        va_list ap;
                    340:
                    341:        va_start(ap, fmt);
                    342:        vsyslog(LOG_ERR, fmt, ap);
                    343:        va_end(ap);
                    344:
                    345:        exit(status);
                    346:        /* NOTREACHED */
1.1       cgd       347: }
                    348:
1.26      joerg     349: static void
1.24      manu      350: logwarn(const char *fmt, ...)
1.1       cgd       351: {
                    352:        va_list ap;
1.20      wiz       353:
1.1       cgd       354:        va_start(ap, fmt);
                    355:        vsyslog(LOG_ERR, fmt, ap);
                    356:        va_end(ap);
1.24      manu      357:        return;
1.1       cgd       358: }

CVSweb <webmaster@jp.NetBSD.org>