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