1 /* $OpenBSD: write.c,v 1.28 2015/01/16 06:40:14 deraadt Exp $ */ 2 /* $NetBSD: write.c,v 1.5 1995/08/31 21:48:32 jtc Exp $ */ 3 4 /* 5 * Copyright (c) 1989, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * This code is derived from software contributed to Berkeley by 9 * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. 10 * 11 * Redistribution and use in source and binary forms, with or without 12 * modification, are permitted provided that the following conditions 13 * are met: 14 * 1. Redistributions of source code must retain the above copyright 15 * notice, this list of conditions and the following disclaimer. 16 * 2. Redistributions in binary form must reproduce the above copyright 17 * notice, this list of conditions and the following disclaimer in the 18 * documentation and/or other materials provided with the distribution. 19 * 3. 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/stat.h> 37 #include <ctype.h> 38 #include <stdio.h> 39 #include <stdlib.h> 40 #include <string.h> 41 #include <signal.h> 42 #include <time.h> 43 #include <fcntl.h> 44 #include <paths.h> 45 #include <pwd.h> 46 #include <unistd.h> 47 #include <limits.h> 48 #include <utmp.h> 49 #include <err.h> 50 #include <vis.h> 51 52 void done(int sig); 53 void do_write(char *, char *, uid_t); 54 void wr_fputs(char *); 55 void search_utmp(char *, char *, int, char *, uid_t); 56 int term_chk(char *, int *, time_t *, int); 57 int utmp_chk(char *, char *); 58 59 int 60 main(int argc, char *argv[]) 61 { 62 char tty[PATH_MAX], *mytty, *cp; 63 int msgsok, myttyfd; 64 time_t atime; 65 uid_t myuid; 66 67 /* check that sender has write enabled */ 68 if (isatty(fileno(stdin))) 69 myttyfd = fileno(stdin); 70 else if (isatty(fileno(stdout))) 71 myttyfd = fileno(stdout); 72 else if (isatty(fileno(stderr))) 73 myttyfd = fileno(stderr); 74 else 75 errx(1, "can't find your tty"); 76 if (!(mytty = ttyname(myttyfd))) 77 errx(1, "can't find your tty's name"); 78 if ((cp = strrchr(mytty, '/'))) 79 mytty = cp + 1; 80 if (term_chk(mytty, &msgsok, &atime, 1)) 81 exit(1); 82 if (!msgsok) 83 warnx("you have write permission turned off"); 84 85 myuid = getuid(); 86 87 /* check args */ 88 switch (argc) { 89 case 2: 90 search_utmp(argv[1], tty, sizeof tty, mytty, myuid); 91 do_write(tty, mytty, myuid); 92 break; 93 case 3: 94 if (!strncmp(argv[2], _PATH_DEV, sizeof(_PATH_DEV) - 1)) 95 argv[2] += sizeof(_PATH_DEV) - 1; 96 if (utmp_chk(argv[1], argv[2])) 97 errx(1, "%s is not logged in on %s", 98 argv[1], argv[2]); 99 if (term_chk(argv[2], &msgsok, &atime, 1)) 100 exit(1); 101 if (myuid && !msgsok) 102 errx(1, "%s has messages disabled on %s", 103 argv[1], argv[2]); 104 do_write(argv[2], mytty, myuid); 105 break; 106 default: 107 (void)fprintf(stderr, "usage: write user [ttyname]\n"); 108 exit(1); 109 } 110 done(0); 111 112 /* NOTREACHED */ 113 return (0); 114 } 115 116 /* 117 * utmp_chk - checks that the given user is actually logged in on 118 * the given tty 119 */ 120 int 121 utmp_chk(char *user, char *tty) 122 { 123 struct utmp u; 124 int ufd; 125 126 if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 127 return(1); /* no utmp, cannot talk to users */ 128 129 while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 130 if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0 && 131 strncmp(tty, u.ut_line, sizeof(u.ut_line)) == 0) { 132 (void)close(ufd); 133 return(0); 134 } 135 136 (void)close(ufd); 137 return(1); 138 } 139 140 /* 141 * search_utmp - search utmp for the "best" terminal to write to 142 * 143 * Ignores terminals with messages disabled, and of the rest, returns 144 * the one with the most recent access time. Returns as value the number 145 * of the user's terminals with messages enabled, or -1 if the user is 146 * not logged in at all. 147 * 148 * Special case for writing to yourself - ignore the terminal you're 149 * writing from, unless that's the only terminal with messages enabled. 150 */ 151 void 152 search_utmp(char *user, char *tty, int ttyl, char *mytty, uid_t myuid) 153 { 154 struct utmp u; 155 time_t bestatime, atime; 156 int ufd, nloggedttys, nttys, msgsok, user_is_me; 157 char atty[UT_LINESIZE + 1]; 158 159 if ((ufd = open(_PATH_UTMP, O_RDONLY)) < 0) 160 err(1, "%s", _PATH_UTMP); 161 162 nloggedttys = nttys = 0; 163 bestatime = 0; 164 user_is_me = 0; 165 while (read(ufd, (char *) &u, sizeof(u)) == sizeof(u)) 166 if (strncmp(user, u.ut_name, sizeof(u.ut_name)) == 0) { 167 ++nloggedttys; 168 (void)strncpy(atty, u.ut_line, UT_LINESIZE); 169 atty[UT_LINESIZE] = '\0'; 170 if (term_chk(atty, &msgsok, &atime, 0)) 171 continue; /* bad term? skip */ 172 if (myuid && !msgsok) 173 continue; /* skip ttys with msgs off */ 174 if (strcmp(atty, mytty) == 0) { 175 user_is_me = 1; 176 continue; /* don't write to yourself */ 177 } 178 ++nttys; 179 if (atime > bestatime) { 180 bestatime = atime; 181 (void)strlcpy(tty, atty, ttyl); 182 } 183 } 184 185 (void)close(ufd); 186 if (nloggedttys == 0) 187 errx(1, "%s is not logged in", user); 188 if (nttys == 0) { 189 if (user_is_me) { /* ok, so write to yourself! */ 190 (void)strlcpy(tty, mytty, ttyl); 191 return; 192 } 193 errx(1, "%s has messages disabled", user); 194 } else if (nttys > 1) 195 warnx("%s is logged in more than once; writing to %s", 196 user, tty); 197 } 198 199 /* 200 * term_chk - check that a terminal exists, and get the message bit 201 * and the access time 202 */ 203 int 204 term_chk(char *tty, int *msgsokP, time_t *atimeP, int showerror) 205 { 206 struct stat s; 207 char path[PATH_MAX]; 208 209 (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 210 if (stat(path, &s) < 0) { 211 if (showerror) 212 warn("%s", path); 213 return(1); 214 } 215 *msgsokP = (s.st_mode & S_IWGRP) != 0; /* group write bit */ 216 *atimeP = s.st_atime; 217 return(0); 218 } 219 220 /* 221 * do_write - actually make the connection 222 */ 223 void 224 do_write(char *tty, char *mytty, uid_t myuid) 225 { 226 char *login, *nows; 227 struct passwd *pwd; 228 time_t now; 229 char path[PATH_MAX], host[HOST_NAME_MAX+1], line[512]; 230 gid_t gid; 231 232 /* Determine our login name before the we reopen() stdout */ 233 if ((login = getlogin()) == NULL) { 234 if ((pwd = getpwuid(myuid))) 235 login = pwd->pw_name; 236 else 237 login = "???"; 238 } 239 240 (void)snprintf(path, sizeof(path), "%s%s", _PATH_DEV, tty); 241 if ((freopen(path, "w", stdout)) == NULL) 242 err(1, "%s", path); 243 244 /* revoke privs, now that we have opened the tty */ 245 gid = getgid(); 246 if (setresgid(gid, gid, gid) == -1) 247 err(1, "setresgid"); 248 249 (void)signal(SIGINT, done); 250 (void)signal(SIGHUP, done); 251 252 /* print greeting */ 253 if (gethostname(host, sizeof(host)) < 0) 254 (void)strlcpy(host, "???", sizeof host); 255 now = time(NULL); 256 nows = ctime(&now); 257 nows[16] = '\0'; 258 (void)printf("\r\n\007\007\007Message from %s@%s on %s at %s ...\r\n", 259 login, host, mytty, nows + 11); 260 261 while (fgets(line, sizeof(line), stdin) != NULL) 262 wr_fputs(line); 263 } 264 265 /* 266 * done - cleanup and exit 267 */ 268 void 269 done(int sig) 270 { 271 (void)write(STDOUT_FILENO, "EOF\r\n", 5); 272 if (sig) 273 _exit(0); 274 else 275 exit(0); 276 } 277 278 /* 279 * wr_fputs - like fputs(), but makes control characters visible and 280 * turns \n into \r\n 281 */ 282 void 283 wr_fputs(char *s) 284 { 285 u_char c; 286 char visout[5], *s2; 287 288 #define PUTC(c) if (putchar(c) == EOF) goto err; 289 290 for (; *s != '\0'; ++s) { 291 c = toascii(*s); 292 if (c == '\n') { 293 PUTC('\r'); 294 PUTC('\n'); 295 continue; 296 } 297 vis(visout, c, VIS_SAFE|VIS_NOSLASH, s[1]); 298 for (s2 = visout; *s2; s2++) 299 PUTC(*s2); 300 } 301 return; 302 303 err: err(1, NULL); 304 #undef PUTC 305 } 306