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