1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 23 /* All Rights Reserved */ 24 25 26 /* 27 * Copyright 2004 Sun Microsystems, Inc. All rights reserved. 28 * Use is subject to license terms. 29 * Copyright (c) 2016 by Delphix. All rights reserved. 30 */ 31 32 #pragma ident "%Z%%M% %I% %E% SMI" 33 34 #include <ctype.h> 35 #include <string.h> 36 #include <stdio.h> 37 #include <signal.h> 38 #include <sys/wait.h> 39 #include <sys/types.h> 40 #include <sys/stat.h> 41 #include <sys/utsname.h> 42 #include <stdlib.h> 43 #include <unistd.h> 44 #include <time.h> 45 #include <utmpx.h> 46 #include <pwd.h> 47 #include <fcntl.h> 48 #include <stdarg.h> 49 #include <locale.h> 50 #include <stdlib.h> 51 #include <limits.h> 52 #include <wctype.h> 53 #include <errno.h> 54 #include <syslog.h> 55 56 #define TRUE 1 57 #define FALSE 0 58 #define FAILURE -1 59 #define DATE_FMT "%a %b %e %H:%M:%S" 60 #define UTMP_HACK /* work around until utmpx is world writable */ 61 /* 62 * DATE-TIME format 63 * %a abbreviated weekday name 64 * %b abbreviated month name 65 * %e day of month 66 * %H hour - 24 hour clock 67 * %M minute 68 * %S second 69 * 70 */ 71 72 static int permit1(int); 73 static int permit(char *); 74 static int readcsi(int, char *, int); 75 static void setsignals(); 76 static void shellcmd(char *); 77 static void openfail(); 78 static void eof(); 79 80 static struct utsname utsn; 81 82 static FILE *fp; /* File pointer for receipient's terminal */ 83 static char *rterm, *receipient; /* Pointer to receipient's terminal & name */ 84 static char *thissys; 85 86 int 87 main(int argc, char **argv) 88 { 89 int i; 90 struct utmpx *ubuf; 91 static struct utmpx self; 92 char ownname[sizeof (self.ut_user) + 1]; 93 static char rterminal[sizeof ("/dev/") + sizeof (self.ut_line)] = 94 "/dev/"; 95 extern char *rterm, *receipient; 96 char *terminal, *ownterminal, *oterminal; 97 short count; 98 extern FILE *fp; 99 char input[134+MB_LEN_MAX]; 100 char *ptr; 101 time_t tod; 102 char time_buf[40]; 103 struct passwd *passptr; 104 char badterm[20][20]; 105 int bad = 0; 106 uid_t myuid; 107 char *bp; 108 int n; 109 wchar_t wc; 110 int c; 111 int newline; 112 113 (void) setlocale(LC_ALL, ""); 114 #if !defined(TEXT_DOMAIN) 115 #define TEXT_DOMAIN "SYS_TEST" 116 #endif 117 (void) textdomain(TEXT_DOMAIN); 118 119 while ((c = getopt(argc, argv, "")) != EOF) 120 switch (c) { 121 case '?': 122 (void) fprintf(stderr, "Usage: write %s\n", 123 gettext("user_name [terminal]")); 124 exit(2); 125 } 126 myuid = geteuid(); 127 uname(&utsn); 128 thissys = utsn.nodename; 129 130 /* Set "rterm" to location where receipient's terminal will go. */ 131 132 rterm = &rterminal[sizeof ("/dev/") - 1]; 133 terminal = NULL; 134 135 if (--argc <= 0) { 136 (void) fprintf(stderr, "Usage: write %s\n", 137 gettext("user_name [terminal]")); 138 exit(1); 139 } 140 else 141 { 142 receipient = *++argv; 143 } 144 145 /* Was a terminal name supplied? If so, save it. */ 146 147 if (--argc > 1) { 148 (void) fprintf(stderr, "Usage: write %s\n", 149 gettext("user_name [terminal]")); 150 exit(1); 151 } else { 152 terminal = *++argv; 153 } 154 155 /* One of the standard file descriptors must be attached to a */ 156 /* terminal in "/dev". */ 157 158 if ((ownterminal = ttyname(fileno(stdin))) == NULL && 159 (ownterminal = ttyname(fileno(stdout))) == NULL && 160 (ownterminal = ttyname(fileno(stderr))) == NULL) { 161 (void) fprintf(stderr, 162 gettext("I cannot determine your terminal name." 163 " No reply possible.\n")); 164 ownterminal = "/dev/???"; 165 } 166 167 /* 168 * Set "ownterminal" past the "/dev/" at the beginning of 169 * the device name. 170 */ 171 oterminal = ownterminal + sizeof ("/dev/")-1; 172 173 /* 174 * Scan through the "utmpx" file for your own entry and the 175 * entry for the person we want to send to. 176 */ 177 for (self.ut_pid = 0, count = 0; (ubuf = getutxent()) != NULL; ) { 178 /* Is this a USER_PROCESS entry? */ 179 180 if (ubuf->ut_type == USER_PROCESS) { 181 /* Is it our entry? (ie. The line matches ours?) */ 182 183 if (strncmp(&ubuf->ut_line[0], oterminal, 184 sizeof (ubuf->ut_line)) == 0) self = *ubuf; 185 186 /* Is this the person we want to send to? */ 187 188 if (strncmp(receipient, &ubuf->ut_user[0], 189 sizeof (ubuf->ut_user)) == 0) { 190 /* If a terminal name was supplied, is this login at the correct */ 191 /* terminal? If not, ignore. If it is right place, copy over the */ 192 /* name. */ 193 194 if (terminal != NULL) { 195 if (strncmp(terminal, &ubuf->ut_line[0], 196 sizeof (ubuf->ut_line)) == 0) { 197 strlcpy(rterm, &ubuf->ut_line[0], 198 sizeof (rterminal) - (rterm - rterminal)); 199 if (myuid && !permit(rterminal)) { 200 bad++; 201 rterm[0] = '\0'; 202 } 203 } 204 } 205 206 /* If no terminal was supplied, then take this terminal if no */ 207 /* other terminal has been encountered already. */ 208 209 else 210 { 211 /* If this is the first encounter, copy the string into */ 212 /* "rterminal". */ 213 214 if (*rterm == '\0') { 215 strlcpy(rterm, &ubuf->ut_line[0], 216 sizeof (rterminal) - (rterm - rterminal)); 217 if (myuid && !permit(rterminal)) { 218 if (bad < 20) { 219 strlcpy(badterm[bad++], rterm, 220 sizeof (badterm[bad++])); 221 } 222 rterm[0] = '\0'; 223 } else if (bad > 0) { 224 (void) fprintf(stderr, 225 gettext( 226 "%s is logged on more than one place.\n" 227 "You are connected to \"%s\".\nOther locations are:\n"), 228 receipient, rterm); 229 for (i = 0; i < bad; i++) 230 (void) fprintf(stderr, "%s\n", badterm[i]); 231 } 232 } 233 234 /* If this is the second terminal, print out the first. In all */ 235 /* cases of multiple terminals, list out all the other terminals */ 236 /* so the user can restart knowing what their choices are. */ 237 238 else if (terminal == NULL) { 239 if (count == 1 && bad == 0) { 240 (void) fprintf(stderr, 241 gettext( 242 "%s is logged on more than one place.\n" 243 "You are connected to \"%s\".\nOther locations are:\n"), 244 receipient, rterm); 245 } 246 fwrite(&ubuf->ut_line[0], sizeof (ubuf->ut_line), 247 1, stderr); 248 (void) fprintf(stderr, "\n"); 249 } 250 251 count++; 252 } /* End of "else" */ 253 } /* End of "else if (strncmp" */ 254 } /* End of "if (USER_PROCESS" */ 255 } /* End of "for(count=0" */ 256 257 /* Did we find a place to talk to? If we were looking for a */ 258 /* specific spot and didn't find it, complain and quit. */ 259 260 if (terminal != NULL && *rterm == '\0') { 261 if (bad > 0) { 262 (void) fprintf(stderr, gettext("Permission denied.\n")); 263 exit(1); 264 } else { 265 #ifdef UTMP_HACK 266 if (strlcat(rterminal, terminal, sizeof (rterminal)) >= 267 sizeof (rterminal)) { 268 (void) fprintf(stderr, 269 gettext("Terminal name too long.\n")); 270 exit(1); 271 } 272 if (self.ut_pid == 0) { 273 if ((passptr = getpwuid(getuid())) == NULL) { 274 (void) fprintf(stderr, 275 gettext("Cannot determine who you are.\n")); 276 exit(1); 277 } 278 (void) strlcpy(&ownname[0], &passptr->pw_name[0], 279 sizeof (ownname)); 280 } else { 281 (void) strlcpy(&ownname[0], self.ut_user, 282 sizeof (self.ut_user)); 283 } 284 if (!permit(rterminal)) { 285 (void) fprintf(stderr, 286 gettext("%s permission denied\n"), terminal); 287 exit(1); 288 } 289 #else 290 (void) fprintf(stderr, gettext("%s is not at \"%s\".\n"), 291 receipient, terminal); 292 exit(1); 293 #endif /* UTMP_HACK */ 294 } 295 } 296 297 /* If we were just looking for anyplace to talk and didn't find */ 298 /* one, complain and quit. */ 299 /* If permissions prevent us from sending to this person - exit */ 300 301 else if (*rterm == '\0') { 302 if (bad > 0) 303 (void) fprintf(stderr, gettext("Permission denied.\n")); 304 else 305 (void) fprintf(stderr, 306 gettext("%s is not logged on.\n"), receipient); 307 exit(1); 308 } 309 310 /* Did we find our own entry? */ 311 312 else if (self.ut_pid == 0) { 313 /* Use the user id instead of utmp name if the entry in the */ 314 /* utmp file couldn't be found. */ 315 316 if ((passptr = getpwuid(getuid())) == (struct passwd *)NULL) { 317 (void) fprintf(stderr, 318 gettext("Cannot determine who you are.\n")); 319 exit(1); 320 } 321 strncpy(&ownname[0], &passptr->pw_name[0], sizeof (ownname)); 322 } 323 else 324 { 325 strncpy(&ownname[0], self.ut_user, sizeof (self.ut_user)); 326 } 327 ownname[sizeof (ownname)-1] = '\0'; 328 329 if (!permit1(1)) 330 (void) fprintf(stderr, 331 gettext("Warning: You have your terminal set to \"mesg -n\"." 332 " No reply possible.\n")); 333 /* Close the utmpx files. */ 334 335 endutxent(); 336 337 /* Try to open up the line to the receipient's terminal. */ 338 339 signal(SIGALRM, openfail); 340 alarm(5); 341 fp = fopen(&rterminal[0], "w"); 342 alarm(0); 343 344 /* Make sure executed subshell doesn't inherit this fd - close-on-exec */ 345 346 if (fcntl(fileno(fp), F_SETFD, FD_CLOEXEC) < 0) { 347 perror("fcntl(F_SETFD)"); 348 exit(1); 349 } 350 351 /* Catch signals SIGHUP, SIGINT, SIGQUIT, and SIGTERM, and send */ 352 /* <EOT> message to receipient before dying away. */ 353 354 setsignals(eof); 355 356 /* Get the time of day, convert it to a string and throw away the */ 357 /* year information at the end of the string. */ 358 359 time(&tod); 360 (void) strftime(time_buf, sizeof (time_buf), 361 dcgettext(NULL, DATE_FMT, LC_TIME), localtime(&tod)); 362 363 (void) fprintf(fp, 364 gettext("\n\007\007\007\tMessage from %s on %s (%s) [ %s ] ...\n"), 365 &ownname[0], thissys, oterminal, time_buf); 366 fflush(fp); 367 (void) fprintf(stderr, "\007\007"); 368 369 /* Get input from user and send to receipient unless it begins */ 370 /* with a !, when it is to be a shell command. */ 371 newline = 1; 372 while ((i = readcsi(0, &input[0], sizeof (input))) > 0) { 373 ptr = &input[0]; 374 /* Is this a shell command? */ 375 376 if ((newline) && (*ptr == '!')) 377 shellcmd(++ptr); 378 379 /* Send line to the receipient. */ 380 381 else { 382 if (myuid && !permit1(fileno(fp))) { 383 (void) fprintf(stderr, 384 gettext("Can no longer write to %s\n"), rterminal); 385 break; 386 } 387 388 /* 389 * All non-printable characters are displayed using a special notation: 390 * Control characters shall be displayed using the two character 391 * sequence of ^ (carat) and the ASCII character - decimal 64 greater 392 * that the character being encoded - eg., a \003 is displayed ^C. 393 * Characters with the eighth bit set shall be displayed using 394 * the three or four character meta notation - e.g., \372 is 395 * displayed M-z and \203 is displayed M-^C. 396 */ 397 398 newline = 0; 399 for (bp = &input[0]; --i >= 0; bp++) { 400 if (*bp == '\n') { 401 newline = 1; 402 putc('\r', fp); 403 } 404 if (*bp == ' ' || 405 *bp == '\t' || *bp == '\n' || 406 *bp == '\r' || *bp == '\013' || 407 *bp == '\007') { 408 putc(*bp, fp); 409 } else if (((n = mbtowc(&wc, bp, MB_CUR_MAX)) > 0) && 410 iswprint(wc)) { 411 for (; n > 0; --n, --i, ++bp) 412 putc(*bp, fp); 413 bp--, ++i; 414 } else { 415 if (!isascii(*bp)) { 416 fputs("M-", fp); 417 *bp = toascii(*bp); 418 } 419 if (iscntrl(*bp)) { 420 putc('^', fp); 421 /* add decimal 64 to the control character */ 422 putc(*bp + 0100, fp); 423 } 424 else 425 putc(*bp, fp); 426 } 427 if (*bp == '\n') 428 fflush(fp); 429 if (ferror(fp) || feof(fp)) { 430 printf(gettext( 431 "\n\007Write failed (%s logged out?)\n"), 432 receipient); 433 exit(1); 434 } 435 } /* for */ 436 fflush(fp); 437 } /* else */ 438 } /* while */ 439 440 /* Since "end of file" received, send <EOT> message to receipient. */ 441 442 eof(); 443 return (0); 444 } 445 446 447 static void 448 setsignals(catch) 449 void (*catch)(); 450 { 451 signal(SIGHUP, catch); 452 signal(SIGINT, catch); 453 signal(SIGQUIT, catch); 454 signal(SIGTERM, catch); 455 } 456 457 458 static void 459 shellcmd(command) 460 char *command; 461 { 462 register pid_t child; 463 extern void eof(); 464 465 if ((child = fork()) == (pid_t)FAILURE) 466 { 467 (void) fprintf(stderr, 468 gettext("Unable to fork. Try again later.\n")); 469 return; 470 } else if (child == (pid_t)0) { 471 /* Reset the signals to the default actions and exec a shell. */ 472 473 if (setgid(getgid()) < 0) 474 exit(1); 475 execl("/usr/bin/sh", "sh", "-c", command, 0); 476 exit(0); 477 } 478 else 479 { 480 /* Allow user to type <del> and <quit> without dying during */ 481 /* commands. */ 482 483 signal(SIGINT, SIG_IGN); 484 signal(SIGQUIT, SIG_IGN); 485 486 /* As parent wait around for user to finish spunoff command. */ 487 488 while (wait(NULL) != child); 489 490 /* Reset the signals to their normal state. */ 491 492 setsignals(eof); 493 } 494 (void) fprintf(stdout, "!\n"); 495 } 496 497 static void 498 openfail() 499 { 500 extern char *rterm, *receipient; 501 502 (void) fprintf(stderr, 503 gettext("Timeout trying to open %s's line(%s).\n"), 504 receipient, rterm); 505 exit(1); 506 } 507 508 static void 509 eof() 510 { 511 extern FILE *fp; 512 513 (void) fprintf(fp, "%s\n", gettext("<EOT>")); 514 exit(0); 515 } 516 517 /* 518 * permit: check mode of terminal - if not writable by all disallow writing to 519 * (even the user cannot therefore write to their own tty) 520 */ 521 522 static int 523 permit(term) 524 char *term; 525 { 526 struct stat buf; 527 int fildes; 528 529 if ((fildes = open(term, O_WRONLY|O_NOCTTY)) < 0) 530 return (0); 531 /* check if the device really is a tty */ 532 if (!isatty(fildes)) { 533 (void) fprintf(stderr, 534 gettext("%s in utmpx is not a tty\n"), term); 535 openlog("write", 0, LOG_AUTH); 536 syslog(LOG_CRIT, "%s in utmpx is not a tty\n", term); 537 closelog(); 538 close(fildes); 539 return (0); 540 } 541 fstat(fildes, &buf); 542 close(fildes); 543 return (buf.st_mode & (S_IWGRP|S_IWOTH)); 544 } 545 546 547 548 /* 549 * permit1: check mode of terminal - if not writable by all disallow writing 550 * to (even the user themself cannot therefore write to their own tty) 551 */ 552 553 /* this is used with fstat (which is faster than stat) where possible */ 554 555 static int 556 permit1(fildes) 557 int fildes; 558 { 559 struct stat buf; 560 561 fstat(fildes, &buf); 562 return (buf.st_mode & (S_IWGRP|S_IWOTH)); 563 } 564 565 566 /* 567 * Read a string of multi-byte characters from specified file. 568 * The requested # of bytes are attempted to read. 569 * readcsi() tries to complete the last multibyte character 570 * by calling mbtowc(), if the leftovers form mbtowc(), 571 * left the last char imcomplete, moves into delta_spool to use later, 572 * next called. The caller must reserve 573 * nbytereq+MB_LEN_MAX bytes for the buffer. When the attempt 574 * is failed, it truncate the last char. 575 * Returns the number of bytes that constitutes the valid multi-byte characters. 576 */ 577 578 579 static int readcsi(d, buf, nbytereq) 580 int d; 581 char *buf; 582 int nbytereq; 583 { 584 static char delta_pool[MB_LEN_MAX * 2]; 585 static char delta_size; 586 char *cp, *nextp, *lastp; 587 int n; 588 int r_size; 589 590 if (delta_size) { 591 memcpy(buf, delta_pool, delta_size); 592 cp = buf + delta_size; 593 r_size = nbytereq - delta_size; 594 } else { 595 cp = buf; 596 r_size = nbytereq; 597 } 598 599 if ((r_size = read(d, cp, r_size)) < 0) 600 r_size = 0; 601 if ((n = delta_size + r_size) <= 0) 602 return (n); 603 604 /* Scan the result to test the completeness of each EUC characters. */ 605 nextp = buf; 606 lastp = buf + n; /* Lastp points to the first junk byte. */ 607 while (nextp < lastp) { 608 if ((n = (lastp - nextp)) > (unsigned int)MB_CUR_MAX) 609 n = (unsigned int)MB_CUR_MAX; 610 if ((n = mbtowc((wchar_t *)0, nextp, n)) <= 0) { 611 if ((lastp - nextp) < (unsigned int)MB_CUR_MAX) 612 break; 613 n = 1; 614 } 615 nextp += n; 616 } 617 /* How many bytes needed to complete the last char? */ 618 delta_size = lastp - nextp; 619 if (delta_size > 0) { 620 if (nextp[delta_size - 1] != '\n') { 621 /* the remnants store into delta_pool */ 622 memcpy(delta_pool, nextp, delta_size); 623 } else 624 nextp = lastp; 625 } 626 *nextp = '\0'; 627 return (nextp-buf); /* Return # of bytes. */ 628 } 629