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