1/* $NetBSD: write.c,v 1.26 2011/08/31 16:24:58 plunky Exp $ */ 2 3/* 4 * Copyright (c) 1989, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * This code is derived from software contributed to Berkeley by 8 * Jef Poskanzer and Craig Leres of the Lawrence Berkeley Laboratory. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 3. Neither the name of the University nor the names of its contributors 19 * may be used to endorse or promote products derived from this software 20 * without specific prior written permission. 21 * 22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND 23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE 26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 32 * SUCH DAMAGE. 33 */ 34 35#include <sys/cdefs.h> 36#ifndef lint 37__COPYRIGHT("@(#) Copyright (c) 1989, 1993\ 38 The Regents of the University of California. All rights reserved."); 39#endif /* not lint */ 40 41#ifndef lint 42#if 0 43static char sccsid[] = "@(#)write.c 8.2 (Berkeley) 4/27/95"; 44#else 45__RCSID("$NetBSD: write.c,v 1.26 2011/08/31 16:24:58 plunky Exp $"); 46#endif 47#endif /* not lint */ 48 49#include <sys/types.h> 50#include <sys/param.h> 51#include <sys/stat.h> 52#include <ctype.h> 53#include <stdio.h> 54#include <stdlib.h> 55#include <string.h> 56#include <signal.h> 57#include <time.h> 58#include <fcntl.h> 59#include <paths.h> 60#include <pwd.h> 61#include <unistd.h> 62#include <err.h> 63#include <errno.h> 64 65#include "utmpentry.h" 66#include "term_chk.h" 67 68__dead static void done(int); 69static void do_write(int, const char *, const uid_t); 70static void wr_fputs(char *); 71static int search_utmp(char *, char *, uid_t, gid_t); 72static int utmp_chk(const char *, const char *); 73 74int 75main(int argc, char **argv) 76{ 77 time_t atime; 78 uid_t myuid, uid; 79 int msgsok, ttyfd; 80 char *mytty; 81 gid_t saved_egid = getegid(); 82 83 if (setegid(getgid()) == -1) 84 err(1, "setegid"); 85 myuid = getuid(); 86 ttyfd = -1; 87 88 mytty = check_sender(&atime, myuid, saved_egid); 89 90 /* check args */ 91 switch (argc) { 92 case 2: 93 ttyfd = search_utmp(argv[1], mytty, myuid, saved_egid); 94 break; 95 case 3: 96 if (!strncmp(argv[2], _PATH_DEV, strlen(_PATH_DEV))) 97 argv[2] += strlen(_PATH_DEV); 98 if (uid_from_user(argv[1], &uid) == -1) 99 errx(1, "%s: unknown user", argv[1]); 100 if (utmp_chk(argv[1], argv[2])) 101 errx(1, "%s is not logged in on %s", 102 argv[1], argv[2]); 103 ttyfd = term_chk(uid, argv[2], &msgsok, &atime, 0, saved_egid); 104 if (ttyfd == -1) 105 err(1, "%s%s", _PATH_DEV, argv[2]); 106 if (myuid && !msgsok) 107 errx(1, "%s has messages disabled on %s", 108 argv[1], argv[2]); 109 break; 110 default: 111 (void)fprintf(stderr, "usage: write user [tty]\n"); 112 exit(1); 113 } 114 if (setgid(getgid()) == -1) 115 err(1, "setgid"); 116 do_write(ttyfd, mytty, myuid); 117 done(0); 118 /* NOTREACHED */ 119#ifdef __GNUC__ 120 return (0); 121#endif 122} 123 124/* 125 * utmp_chk - checks that the given user is actually logged in on 126 * the given tty 127 */ 128static int 129utmp_chk(const char *user, const char *tty) 130{ 131 struct utmpentry *ep; 132 133 (void)getutentries(NULL, &ep); 134 135 for (; ep; ep = ep->next) 136 if (strcmp(user, ep->name) == 0 && strcmp(tty, ep->line) == 0) 137 return(0); 138 return(1); 139} 140 141/* 142 * search_utmp - search utmp for the "best" terminal to write to 143 * 144 * Ignores terminals with messages disabled, and of the rest, returns 145 * the one with the most recent access time. Returns as value the number 146 * of the user's terminals with messages enabled, or -1 if the user is 147 * not logged in at all. 148 * 149 * Special case for writing to yourself - ignore the terminal you're 150 * writing from, unless that's the only terminal with messages enabled. 151 */ 152static int 153search_utmp(char *user, char *mytty, uid_t myuid, gid_t saved_egid) 154{ 155 char tty[MAXPATHLEN]; 156 time_t bestatime, atime; 157 int nloggedttys, nttys, msgsok, user_is_me; 158 struct utmpentry *ep; 159 int fd, nfd; 160 uid_t uid; 161 162 if (uid_from_user(user, &uid) == -1) 163 errx(1, "%s: unknown user", user); 164 165 (void)getutentries(NULL, &ep); 166 167 nloggedttys = nttys = 0; 168 bestatime = 0; 169 user_is_me = 0; 170 fd = -1; 171 for (; ep; ep = ep->next) 172 if (strcmp(user, ep->name) == 0) { 173 ++nloggedttys; 174 nfd = term_chk(uid, ep->line, &msgsok, &atime, 0, 175 saved_egid); 176 if (nfd == -1) 177 continue; /* bad term? skip */ 178 if (myuid && !msgsok) { 179 close(nfd); 180 continue; /* skip ttys with msgs off */ 181 } 182 if (strcmp(ep->line, mytty) == 0) { 183 user_is_me = 1; 184 if (fd == -1) 185 fd = nfd; 186 else 187 close(nfd); 188 continue; /* don't write to yourself */ 189 } 190 ++nttys; 191 if (atime > bestatime) { 192 bestatime = atime; 193 (void)strlcpy(tty, ep->line, sizeof(tty)); 194 close(fd); 195 fd = nfd; 196 } else 197 close(nfd); 198 } 199 200 if (nloggedttys == 0) 201 errx(1, "%s is not logged in", user); 202 if (nttys == 0) { 203 if (user_is_me) /* ok, so write to yourself! */ 204 return fd; 205 errx(1, "%s has messages disabled", user); 206 } else if (nttys > 1) 207 warnx("%s is logged in more than once; writing to %s", 208 user, tty); 209 return fd; 210} 211 212/* 213 * do_write - actually make the connection 214 */ 215static void 216do_write(int ttyfd, const char *mytty, const uid_t myuid) 217{ 218 const char *login; 219 char *nows; 220 struct passwd *pwd; 221 time_t now; 222 char host[MAXHOSTNAMELEN + 1], line[512]; 223 224 /* Determine our login name before we re-open stdout */ 225 if ((login = getlogin()) == NULL) { 226 if ((pwd = getpwuid(myuid)) != NULL) 227 login = pwd->pw_name; 228 else login = "???"; 229 } 230 231 if (dup2(ttyfd, STDOUT_FILENO) == -1) 232 err(1, "dup2"); 233 234 (void)signal(SIGINT, done); 235 (void)signal(SIGHUP, done); 236 (void)close(ttyfd); 237 238 /* print greeting */ 239 if (gethostname(host, sizeof(host)) < 0) 240 (void)strlcpy(host, "???", sizeof(host)); 241 else 242 host[sizeof(host) - 1] = '\0'; 243 now = time(NULL); 244 nows = ctime(&now); 245 nows[16] = '\0'; 246 (void)printf("\r\n\a\a\aMessage from %s@%s on %s at %s ...\r\n", 247 login, host, mytty, nows + 11); 248 249 while (fgets(line, sizeof(line), stdin) != NULL) 250 wr_fputs(line); 251} 252 253/* 254 * done - cleanup and exit 255 */ 256static void 257done(int signo) 258{ 259 260 (void)write(STDOUT_FILENO, "EOF\r\n", sizeof("EOF\r\n") - 1); 261 if (signo == 0) 262 exit(0); 263 else 264 _exit(0); 265} 266 267/* 268 * wr_fputs - like fputs(), but makes control characters visible and 269 * turns \n into \r\n 270 */ 271static void 272wr_fputs(char *s) 273{ 274 unsigned char c; 275 276#define PUTC(c) if (putchar(c) == EOF) goto err; 277 278 for (; *s != '\0'; ++s) { 279 c = toascii(*s); 280 if (c == '\n') { 281 PUTC('\r'); 282 } else if (!isprint(c) && !isspace(c) && c != '\a') { 283 PUTC('^'); 284 c ^= 0x40; /* DEL to ?, others to alpha */ 285 } 286 PUTC(c); 287 } 288 return; 289 290err: err(1, NULL); 291#undef PUTC 292} 293