1 /*- 2 * SPDX-License-Identifier: BSD-3-Clause 3 * 4 * Copyright (c) 1980, 1993 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/param.h> 33 #include <sys/socket.h> 34 #include <sys/stat.h> 35 #include <sys/file.h> 36 #include <sys/wait.h> 37 38 #include <netinet/in.h> 39 40 #include <ctype.h> 41 #include <err.h> 42 #include <errno.h> 43 #include <netdb.h> 44 #include <paths.h> 45 #include <pwd.h> 46 #include <termios.h> 47 #include <signal.h> 48 #include <stdio.h> 49 #include <stdlib.h> 50 #include <string.h> 51 #include <syslog.h> 52 #include <unistd.h> 53 #include <utmpx.h> 54 55 static int debug = 0; 56 #define dsyslog if (debug) syslog 57 58 #define MAXIDLE 120 59 60 static char hostname[MAXHOSTNAMELEN]; 61 62 static void jkfprintf(FILE *, char[], off_t); 63 static void mailfor(char *); 64 static void notify(struct utmpx *, char[], off_t, int); 65 static void reapchildren(int); 66 67 int 68 main(int argc __unused, char *argv[] __unused) 69 { 70 struct sockaddr_in from; 71 socklen_t fromlen; 72 int cc; 73 char msgbuf[256]; 74 75 /* verify proper invocation */ 76 fromlen = sizeof(from); 77 if (getsockname(0, (struct sockaddr *)&from, &fromlen) < 0) 78 err(1, "getsockname"); 79 openlog("comsat", LOG_PID, LOG_DAEMON); 80 if (chdir(_PATH_MAILDIR)) { 81 syslog(LOG_ERR, "chdir: %s: %m", _PATH_MAILDIR); 82 (void) recv(0, msgbuf, sizeof(msgbuf) - 1, 0); 83 exit(1); 84 } 85 (void)gethostname(hostname, sizeof(hostname)); 86 (void)signal(SIGTTOU, SIG_IGN); 87 (void)signal(SIGCHLD, reapchildren); 88 for (;;) { 89 cc = recv(0, msgbuf, sizeof(msgbuf) - 1, 0); 90 if (cc <= 0) { 91 if (errno != EINTR) 92 sleep(1); 93 errno = 0; 94 continue; 95 } 96 msgbuf[cc] = '\0'; 97 mailfor(msgbuf); 98 sigsetmask(0L); 99 } 100 } 101 102 static void 103 reapchildren(int signo __unused) 104 { 105 while (wait3(NULL, WNOHANG, NULL) > 0); 106 } 107 108 static void 109 mailfor(char *name) 110 { 111 struct utmpx *utp; 112 char *cp; 113 char *file; 114 off_t offset; 115 int folder; 116 char buf[MAXPATHLEN]; 117 118 if ((cp = strchr(name, '@')) == NULL) 119 return; 120 *cp = '\0'; 121 offset = strtoll(cp + 1, NULL, 10); 122 if ((cp = strchr(cp + 1, ':')) != NULL && 123 strchr((file = cp + 1), '/') == NULL) { 124 snprintf(buf, sizeof(buf), "%s/%s", _PATH_MAILDIR, file); 125 folder = 1; 126 } else { 127 snprintf(buf, sizeof(buf), "%s/%s", _PATH_MAILDIR, name); 128 folder = 0; 129 } 130 setutxent(); 131 while ((utp = getutxent()) != NULL) 132 if (utp->ut_type == USER_PROCESS && !strcmp(utp->ut_user, name)) 133 notify(utp, buf, offset, folder); 134 endutxent(); 135 } 136 137 static const char *cr; 138 139 static void 140 notify(struct utmpx *utp, char file[], off_t offset, int folder) 141 { 142 FILE *tp; 143 struct stat stb; 144 struct termios tio; 145 struct passwd *p; 146 char tty[20]; 147 const char *s = utp->ut_line; 148 149 if (strncmp(s, "pts/", 4) == 0) 150 s += 4; 151 if (strchr(s, '/')) { 152 /* A slash is an attempt to break security... */ 153 syslog(LOG_AUTH | LOG_NOTICE, "Unexpected `/' in `%s'", 154 utp->ut_line); 155 return; 156 } 157 (void)snprintf(tty, sizeof(tty), "%s%s", _PATH_DEV, utp->ut_line); 158 if (stat(tty, &stb) == -1 || !(stb.st_mode & (S_IXUSR | S_IXGRP))) { 159 dsyslog(LOG_DEBUG, "%s: wrong mode on %s", utp->ut_user, tty); 160 return; 161 } 162 dsyslog(LOG_DEBUG, "notify %s on %s", utp->ut_user, tty); 163 switch (fork()) { 164 case -1: 165 syslog(LOG_NOTICE, "fork failed (%m)"); 166 return; 167 case 0: 168 break; 169 default: 170 return; 171 } 172 if ((tp = fopen(tty, "w")) == NULL) { 173 dsyslog(LOG_ERR, "%s: %s", tty, strerror(errno)); 174 _exit(1); 175 } 176 (void)tcgetattr(fileno(tp), &tio); 177 cr = ((tio.c_oflag & (OPOST|ONLCR)) == (OPOST|ONLCR)) ? "\n" : "\n\r"; 178 179 /* Set uid/gid/groups to user's in case mail drop is on nfs */ 180 if ((p = getpwnam(utp->ut_user)) == NULL || 181 initgroups(p->pw_name, p->pw_gid) == -1 || 182 setgid(p->pw_gid) == -1 || 183 setuid(p->pw_uid) == -1) 184 _exit(1); 185 186 if (stb.st_mode & S_IXUSR) { 187 (void)fprintf(tp, 188 "%s\007New mail for %s@%.*s\007 has arrived%s%s%s:%s----%s", 189 cr, utp->ut_user, (int)sizeof(hostname), hostname, 190 folder ? cr : "", folder ? "to " : "", folder ? file : "", 191 cr, cr); 192 jkfprintf(tp, file, offset); 193 } else if (stb.st_mode & S_IXGRP) { 194 (void)fprintf(tp, "\007"); 195 (void)fflush(tp); 196 (void)sleep(1); 197 (void)fprintf(tp, "\007"); 198 } 199 (void)fclose(tp); 200 _exit(0); 201 } 202 203 static void 204 jkfprintf(FILE *tp, char file[], off_t offset) 205 { 206 unsigned char *cp, ch; 207 FILE *fi; 208 int linecnt, charcnt, inheader; 209 unsigned char line[BUFSIZ]; 210 211 if ((fi = fopen(file, "r")) == NULL) 212 return; 213 214 (void)fseeko(fi, offset, SEEK_CUR); 215 /* 216 * Print the first 7 lines or 560 characters of the new mail 217 * (whichever comes first). Skip header crap other than 218 * From, Subject, To, and Date. 219 */ 220 linecnt = 7; 221 charcnt = 560; 222 inheader = 1; 223 while (fgets(line, sizeof(line), fi) != NULL) { 224 if (inheader) { 225 if (line[0] == '\n') { 226 inheader = 0; 227 continue; 228 } 229 if (line[0] == ' ' || line[0] == '\t' || 230 (strncmp(line, "From:", 5) && 231 strncmp(line, "Subject:", 8))) 232 continue; 233 } 234 if (linecnt <= 0 || charcnt <= 0) { 235 (void)fprintf(tp, "...more...%s", cr); 236 (void)fclose(fi); 237 return; 238 } 239 /* strip weird stuff so can't trojan horse stupid terminals */ 240 for (cp = line; (ch = *cp) && ch != '\n'; ++cp, --charcnt) { 241 /* disable upper controls and enable all other 242 8bit codes due to lack of locale knowledge 243 */ 244 if (((ch & 0x80) && ch < 0xA0) || 245 (!(ch & 0x80) && !isprint(ch) && 246 !isspace(ch) && ch != '\a' && ch != '\b') 247 ) { 248 if (ch & 0x80) { 249 ch &= ~0x80; 250 (void)fputs("M-", tp); 251 } 252 if (iscntrl(ch)) { 253 ch ^= 0x40; 254 (void)fputc('^', tp); 255 } 256 } 257 (void)fputc(ch, tp); 258 } 259 (void)fputs(cr, tp); 260 --linecnt; 261 } 262 (void)fprintf(tp, "----%s\n", cr); 263 (void)fclose(fi); 264 } 265