1 /* $NetBSD: mail.local.c,v 1.26 2011/08/27 15:40:31 joerg Exp $ */ 2 3 /*- 4 * Copyright (c) 1990, 1993, 1994 5 * The Regents of the University of California. All rights reserved. 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. 15 * 3. Neither the name of the University nor the names of its contributors 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 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\ 35 The Regents of the University of California. All rights reserved."); 36 #if 0 37 static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95"; 38 #else 39 __RCSID("$NetBSD: mail.local.c,v 1.26 2011/08/27 15:40:31 joerg Exp $"); 40 #endif 41 #endif /* not lint */ 42 43 #include <sys/param.h> 44 #include <sys/stat.h> 45 #include <sys/socket.h> 46 47 #include <netinet/in.h> 48 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <pwd.h> 52 #include <netdb.h> 53 #include <stdarg.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <syslog.h> 58 #include <time.h> 59 #include <unistd.h> 60 #include <sysexits.h> 61 62 63 #include "pathnames.h" 64 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); 71 72 int 73 main(int argc, char *argv[]) 74 { 75 struct passwd *pw; 76 int ch, fd, eval, lockfile = 0; 77 uid_t uid; 78 const char *from; 79 80 /* use a reasonable umask */ 81 (void) umask(0077); 82 83 openlog("mail.local", LOG_PERROR, LOG_MAIL); 84 85 from = NULL; 86 while ((ch = getopt(argc, argv, "ldf:r:")) != -1) 87 switch (ch) { 88 case 'd': /* backward compatible */ 89 break; 90 case 'f': 91 case 'r': /* backward compatible */ 92 if (from) 93 logerr(EX_USAGE, "multiple -f options"); 94 from = optarg; 95 break; 96 case 'l': 97 lockfile++; 98 break; 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); 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 } 127 exit (eval); 128 } 129 130 static int 131 store(const char *from) 132 { 133 FILE *fp = NULL; /* XXX gcc */ 134 time_t tval; 135 int fd, eline; 136 char *tn, line[2048]; 137 138 tn = strdup(_PATH_LOCTMP); 139 if (!tn) 140 logerr(EX_OSERR, "not enough core"); 141 if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+"))) 142 logerr(EX_OSERR, "unable to open temporary file"); 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 { 154 if (eline && line[0] == 'F' && !memcmp(line, "From ", 5)) 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)) 171 logerr(EX_OSERR, "temporary file write error"); 172 fd = dup(fd); 173 (void)fclose(fp); 174 return(fd); 175 } 176 177 static int 178 deliver(int fd, char *name, int lockfile) 179 { 180 struct stat sb; 181 struct passwd pwres, *pw; 182 char pwbuf[1024]; 183 int created, mbfd, nr, nw, off, rval=EX_OK, lfd=-1; 184 char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN]; 185 off_t curoff; 186 187 /* 188 * Disallow delivery to unknown names -- special mailboxes can be 189 * handled in the sendmail aliases file. 190 */ 191 if ((getpwnam_r(name, &pwres, pwbuf, sizeof(pwbuf), &pw)) != 0) { 192 logwarn("unable to find user %s: %s", name, strerror(errno)); 193 return(EX_TEMPFAIL); 194 } 195 if (pw == NULL) { 196 logwarn("unknown name: %s", name); 197 return(EX_NOUSER); 198 } 199 200 (void)snprintf(path, sizeof path, "%s/%s", _PATH_MAILDIR, name); 201 202 if (lockfile) { 203 (void)snprintf(lpath, sizeof lpath, "%s/%s.lock", 204 _PATH_MAILDIR, name); 205 206 if((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL, 207 S_IRUSR|S_IWUSR)) < 0) { 208 logwarn("%s: %s", lpath, strerror(errno)); 209 return(EX_OSERR); 210 } 211 } 212 213 if (!(created = lstat(path, &sb)) && 214 (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) { 215 logwarn("%s: linked file", path); 216 return(EX_OSERR); 217 } 218 219 if ((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK, 220 S_IRUSR|S_IWUSR)) < 0) { 221 if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK, 222 S_IRUSR|S_IWUSR)) < 0) { 223 logwarn("%s: %s", path, strerror(errno)); 224 return(EX_OSERR); 225 } 226 } 227 228 curoff = lseek(mbfd, 0, SEEK_END); 229 (void)snprintf(biffmsg, sizeof biffmsg, "%s@%lld\n", name, 230 (long long)curoff); 231 if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { 232 logwarn("temporary file: %s", strerror(errno)); 233 rval = EX_OSERR; 234 goto bad; 235 } 236 237 while ((nr = read(fd, buf, sizeof(buf))) > 0) 238 for (off = 0; off < nr; off += nw) 239 if ((nw = write(mbfd, buf + off, nr - off)) < 0) { 240 logwarn("%s: %s", path, strerror(errno)); 241 goto trunc; 242 } 243 if (nr < 0) { 244 logwarn("temporary file: %s", strerror(errno)); 245 trunc: (void)ftruncate(mbfd, curoff); 246 rval = EX_OSERR; 247 } 248 249 /* 250 * Set the owner and group. Historically, binmail repeated this at 251 * each mail delivery. We no longer do this, assuming that if the 252 * ownership or permissions were changed there was a reason for doing 253 * so. 254 */ 255 bad: 256 if (lockfile) { 257 if (lfd >= 0) { 258 unlink(lpath); 259 close(lfd); 260 } 261 } 262 if (created) 263 (void)fchown(mbfd, pw->pw_uid, pw->pw_gid); 264 265 (void)fsync(mbfd); /* Don't wait for update. */ 266 (void)close(mbfd); /* Implicit unlock. */ 267 268 if (rval == EX_OK) 269 notifybiff(biffmsg); 270 271 return rval; 272 } 273 274 void 275 notifybiff(char *msg) 276 { 277 static struct sockaddr_in addr; 278 static int f = -1; 279 struct hostent *hp; 280 struct servent *sp; 281 int len; 282 283 if (!addr.sin_family) { 284 /* Be silent if biff service not available. */ 285 if (!(sp = getservbyname("biff", "udp"))) 286 return; 287 if (!(hp = gethostbyname("localhost"))) { 288 logwarn("localhost: %s", strerror(errno)); 289 return; 290 } 291 addr.sin_len = sizeof(struct sockaddr_in); 292 addr.sin_family = hp->h_addrtype; 293 addr.sin_port = sp->s_port; 294 memcpy(&addr.sin_addr, hp->h_addr, hp->h_length); 295 } 296 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 297 logwarn("socket: %s", strerror(errno)); 298 return; 299 } 300 len = strlen(msg) + 1; 301 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) 302 != len) 303 logwarn("sendto biff: %s", strerror(errno)); 304 } 305 306 static void 307 usage(void) 308 { 309 logerr(EX_USAGE, "usage: mail.local [-l] [-f from] user ..."); 310 } 311 312 static void 313 logerr(int status, const char *fmt, ...) 314 { 315 va_list ap; 316 317 va_start(ap, fmt); 318 vsyslog(LOG_ERR, fmt, ap); 319 va_end(ap); 320 321 exit(status); 322 /* NOTREACHED */ 323 } 324 325 static void 326 logwarn(const char *fmt, ...) 327 { 328 va_list ap; 329 330 va_start(ap, fmt); 331 vsyslog(LOG_ERR, fmt, ap); 332 va_end(ap); 333 return; 334 } 335