1 /* $NetBSD: mail.local.c,v 1.23 2006/09/27 17:15:20 christos 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. Neither the name of the University nor the names of its contributors 16 * may be used to endorse or promote products derived from this software 17 * without specific prior written permission. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 20 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 21 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 22 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 23 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 24 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 25 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 26 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 27 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 28 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 29 * SUCH DAMAGE. 30 */ 31 32 #include <sys/cdefs.h> 33 #ifndef lint 34 __COPYRIGHT("@(#) Copyright (c) 1990, 1993, 1994\n\ 35 The Regents of the University of California. All rights reserved.\n"); 36 #if 0 37 static char sccsid[] = "@(#)mail.local.c 8.22 (Berkeley) 6/21/95"; 38 #else 39 __RCSID("$NetBSD: mail.local.c,v 1.23 2006/09/27 17:15:20 christos Exp $"); 40 #endif 41 #endif /* not lint */ 42 43 #include <sys/param.h> 44 #include <sys/stat.h> 45 #include <sys/socket.h> 46 47 #include <netinet/in.h> 48 49 #include <errno.h> 50 #include <fcntl.h> 51 #include <pwd.h> 52 #include <netdb.h> 53 #include <stdarg.h> 54 #include <stdio.h> 55 #include <stdlib.h> 56 #include <string.h> 57 #include <syslog.h> 58 #include <time.h> 59 #include <unistd.h> 60 61 #include "pathnames.h" 62 63 #define FATAL 1 64 #define NOTFATAL 0 65 66 int deliver __P((int, char *, int)); 67 void err __P((int, const char *, ...)) 68 __attribute__((__format__(__printf__, 2, 3))); 69 void notifybiff __P((char *)); 70 int store __P((const char *)); 71 void usage __P((void)); 72 int main __P((int, char **)); 73 74 int 75 main(argc, argv) 76 int argc; 77 char **argv; 78 { 79 struct passwd *pw; 80 int ch, fd, eval, lockfile = 0; 81 uid_t uid; 82 const char *from; 83 84 /* use a reasonable umask */ 85 (void) umask(0077); 86 87 openlog("mail.local", LOG_PERROR, LOG_MAIL); 88 89 from = NULL; 90 while ((ch = getopt(argc, argv, "ldf:r:")) != -1) 91 switch (ch) { 92 case 'd': /* backward compatible */ 93 break; 94 case 'f': 95 case 'r': /* backward compatible */ 96 if (from) 97 err(FATAL, "multiple -f options"); 98 from = optarg; 99 break; 100 case 'l': 101 lockfile++; 102 break; 103 case '?': 104 default: 105 usage(); 106 } 107 argc -= optind; 108 argv += optind; 109 110 if (!*argv) 111 usage(); 112 113 /* 114 * If from not specified, use the name from getlogin() if the 115 * uid matches, otherwise, use the name from the password file 116 * corresponding to the uid. 117 */ 118 uid = getuid(); 119 if (!from && (!(from = getlogin()) || 120 !(pw = getpwnam(from)) || pw->pw_uid != uid)) 121 from = (pw = getpwuid(uid)) ? pw->pw_name : "???"; 122 123 fd = store(from); 124 for (eval = 0; *argv; ++argv) 125 eval |= deliver(fd, *argv, lockfile); 126 exit (eval); 127 } 128 129 int 130 store(from) 131 const char *from; 132 { 133 FILE *fp = NULL; /* XXX gcc */ 134 time_t tval; 135 int fd, eline; 136 char *tn, line[2048]; 137 138 tn = strdup(_PATH_LOCTMP); 139 if (!tn) 140 err(FATAL, "not enough core"); 141 if ((fd = mkstemp(tn)) == -1 || !(fp = fdopen(fd, "w+"))) 142 err(FATAL, "unable to open temporary file"); 143 (void)unlink(tn); 144 free(tn); 145 146 (void)time(&tval); 147 (void)fprintf(fp, "From %s %s", from, ctime(&tval)); 148 149 line[0] = '\0'; 150 for (eline = 1; fgets(line, sizeof(line), stdin);) { 151 if (line[0] == '\n') 152 eline = 1; 153 else { 154 if (eline && line[0] == 'F' && !memcmp(line, "From ", 5)) 155 (void)putc('>', fp); 156 eline = 0; 157 } 158 (void)fprintf(fp, "%s", line); 159 if (ferror(fp)) 160 break; 161 } 162 163 /* If message not newline terminated, need an extra. */ 164 if (!index(line, '\n')) 165 (void)putc('\n', fp); 166 /* Output a newline; note, empty messages are allowed. */ 167 (void)putc('\n', fp); 168 169 (void)fflush(fp); 170 if (ferror(fp)) 171 err(FATAL, "temporary file write error"); 172 fd = dup(fd); 173 (void)fclose(fp); 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