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