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