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[] = "from: @(#)mail.local.c 5.6 (Berkeley) 6/19/91";*/ 42 static char rcsid[] = "$Id: mail.local.c,v 1.8 1994/08/05 23:41:15 jtc Exp $"; 43 #endif /* not lint */ 44 45 #include <sys/param.h> 46 #include <sys/stat.h> 47 #include <sys/socket.h> 48 #include <netinet/in.h> 49 #include <syslog.h> 50 #include <fcntl.h> 51 #include <netdb.h> 52 #include <pwd.h> 53 #include <time.h> 54 #include <unistd.h> 55 #include <errno.h> 56 #include <stdio.h> 57 #include <stdlib.h> 58 #include <string.h> 59 #include "pathnames.h" 60 61 #define FATAL 1 62 #define NOTFATAL 0 63 64 int deliver __P((int, char *, int)); 65 void err __P((int, const char *, ...)); 66 void notifybiff __P((char *)); 67 int store __P((char *)); 68 void usage __P((void)); 69 70 main(argc, argv) 71 int argc; 72 char **argv; 73 { 74 extern int optind; 75 extern char *optarg; 76 struct passwd *pw; 77 int ch, fd, eval, lockfile=0; 78 uid_t uid; 79 char *from; 80 81 openlog("mail.local", LOG_PERROR, LOG_MAIL); 82 83 from = NULL; 84 while ((ch = getopt(argc, argv, "ldf:r:")) != EOF) 85 switch(ch) { 86 case 'd': /* backward compatible */ 87 break; 88 case 'f': 89 case 'r': /* backward compatible */ 90 if (from) 91 err(FATAL, "multiple -f options"); 92 from = optarg; 93 break; 94 case 'l': 95 lockfile++; 96 break; 97 case '?': 98 default: 99 usage(); 100 } 101 argc -= optind; 102 argv += optind; 103 104 if (!*argv) 105 usage(); 106 107 /* 108 * If from not specified, use the name from getlogin() if the 109 * uid matches, otherwise, use the name from the password file 110 * corresponding to the uid. 111 */ 112 uid = getuid(); 113 if (!from && (!(from = getlogin()) || 114 !(pw = getpwnam(from)) || pw->pw_uid != uid)) 115 from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 116 117 fd = store(from); 118 for (eval = 0; *argv; ++argv) 119 eval |= deliver(fd, *argv, lockfile); 120 exit(eval); 121 } 122 123 store(from) 124 char *from; 125 { 126 FILE *fp; 127 time_t tval; 128 int fd, eline; 129 char *tn, line[2048]; 130 131 tn = strdup(_PATH_LOCTMP); 132 if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+"))) 133 err(FATAL, "unable to open temporary file"); 134 (void)unlink(tn); 135 free(tn); 136 137 (void)time(&tval); 138 (void)fprintf(fp, "From %s %s", from, ctime(&tval)); 139 140 line[0] = '\0'; 141 for (eline = 1; fgets(line, sizeof(line), stdin);) { 142 if (line[0] == '\n') 143 eline = 1; 144 else { 145 if (eline && line[0] == 'F' && !bcmp(line, "From ", 5)) 146 (void)putc('>', fp); 147 eline = 0; 148 } 149 (void)fprintf(fp, "%s", line); 150 if (ferror(fp)) 151 break; 152 } 153 154 /* If message not newline terminated, need an extra. */ 155 if (!index(line, '\n')) 156 (void)putc('\n', fp); 157 /* Output a newline; note, empty messages are allowed. */ 158 (void)putc('\n', fp); 159 160 (void)fflush(fp); 161 if (ferror(fp)) 162 err(FATAL, "temporary file write error"); 163 return(fd); 164 } 165 166 deliver(fd, name, lockfile) 167 int fd; 168 char *name; 169 int lockfile; 170 { 171 struct stat sb; 172 struct passwd *pw; 173 int created, mbfd, nr, nw, off, rval=0, lfd=-1; 174 char biffmsg[100], buf[8*1024], path[MAXPATHLEN], lpath[MAXPATHLEN]; 175 off_t curoff; 176 177 /* 178 * Disallow delivery to unknown names -- special mailboxes can be 179 * handled in the sendmail aliases file. 180 */ 181 if (!(pw = getpwnam(name))) { 182 err(NOTFATAL, "unknown name: %s", name); 183 return(1); 184 } 185 186 (void)sprintf(path, "%s/%s", _PATH_MAILDIR, name); 187 188 if(lockfile) { 189 (void)sprintf(lpath, "%s/%s.lock", _PATH_MAILDIR, name); 190 191 if((lfd = open(lpath, O_CREAT|O_WRONLY|O_EXCL, 192 S_IRUSR|S_IWUSR)) < 0) { 193 err(NOTFATAL, "%s: %s", lpath, strerror(errno)); 194 return(1); 195 } 196 } 197 198 if (!(created = lstat(path, &sb)) && 199 (sb.st_nlink != 1 || S_ISLNK(sb.st_mode))) { 200 err(NOTFATAL, "%s: linked file", path); 201 return(1); 202 } 203 if((mbfd = open(path, O_APPEND|O_WRONLY|O_EXLOCK, 204 S_IRUSR|S_IWUSR)) < 0) { 205 if ((mbfd = open(path, O_APPEND|O_CREAT|O_WRONLY|O_EXLOCK, 206 S_IRUSR|S_IWUSR)) < 0) { 207 err(NOTFATAL, "%s: %s", path, strerror(errno)); 208 return(1); 209 } 210 } 211 212 curoff = lseek(mbfd, 0, SEEK_END); 213 (void)sprintf(biffmsg, "%s@%qd\n", name, curoff); 214 if (lseek(fd, 0, SEEK_SET) == (off_t)-1) { 215 err(FATAL, "temporary file: %s", strerror(errno)); 216 rval = 1; 217 goto bad; 218 } 219 220 while ((nr = read(fd, buf, sizeof(buf))) > 0) 221 for (off = 0; off < nr; off += nw) 222 if ((nw = write(mbfd, buf + off, nr - off)) < 0) { 223 err(NOTFATAL, "%s: %s", path, strerror(errno)); 224 goto trunc; 225 } 226 if (nr < 0) { 227 err(FATAL, "temporary file: %s", strerror(errno)); 228 trunc: (void)ftruncate(mbfd, curoff); 229 rval = 1; 230 } 231 232 /* 233 * Set the owner and group. Historically, binmail repeated this at 234 * each mail delivery. We no longer do this, assuming that if the 235 * ownership or permissions were changed there was a reason for doing 236 * so. 237 */ 238 bad: 239 if(lockfile) { 240 if(lfd >= 0) { 241 unlink(lpath); 242 close(lfd); 243 } 244 } 245 if (created) 246 (void)fchown(mbfd, pw->pw_uid, pw->pw_gid); 247 248 (void)fsync(mbfd); /* Don't wait for update. */ 249 (void)close(mbfd); /* Implicit unlock. */ 250 251 if (!rval) 252 notifybiff(biffmsg); 253 return(rval); 254 } 255 256 void 257 notifybiff(msg) 258 char *msg; 259 { 260 static struct sockaddr_in addr; 261 static int f = -1; 262 struct hostent *hp; 263 struct servent *sp; 264 int len; 265 266 if (!addr.sin_family) { 267 /* Be silent if biff service not available. */ 268 if (!(sp = getservbyname("biff", "udp"))) 269 return; 270 if (!(hp = gethostbyname("localhost"))) { 271 err(NOTFATAL, "localhost: %s", strerror(errno)); 272 return; 273 } 274 addr.sin_family = hp->h_addrtype; 275 bcopy(hp->h_addr, &addr.sin_addr, hp->h_length); 276 addr.sin_port = sp->s_port; 277 } 278 if (f < 0 && (f = socket(AF_INET, SOCK_DGRAM, 0)) == -1) { 279 err(NOTFATAL, "socket: %s", strerror(errno)); 280 return; 281 } 282 len = strlen(msg) + 1; 283 if (sendto(f, msg, len, 0, (struct sockaddr *)&addr, sizeof(addr)) 284 != len) 285 err(NOTFATAL, "sendto biff: %s", strerror(errno)); 286 } 287 288 void 289 usage() 290 { 291 err(FATAL, "usage: mail.local [-f from] user ..."); 292 } 293 294 #if __STDC__ 295 #include <stdarg.h> 296 #else 297 #include <varargs.h> 298 #endif 299 300 void 301 #if __STDC__ 302 err(int isfatal, const char *fmt, ...) 303 #else 304 err(isfatal, fmt) 305 int isfatal; 306 char *fmt; 307 va_dcl 308 #endif 309 { 310 va_list ap; 311 #if __STDC__ 312 va_start(ap, fmt); 313 #else 314 va_start(ap); 315 #endif 316 vsyslog(LOG_ERR, fmt, ap); 317 va_end(ap); 318 if (isfatal) 319 exit(1); 320 } 321