1 /* 2 * Copyright (c) 1988 Regents of the University of California. 3 * All rights reserved. 4 * 5 * Redistribution and use in source and binary forms are permitted 6 * provided that the above copyright notice and this paragraph are 7 * duplicated in all such forms and that any documentation, 8 * advertising materials, and other materials related to such 9 * distribution and use acknowledge that the software was developed 10 * by the University of California, Berkeley. The name of the 11 * University may not be used to endorse or promote products derived 12 * from this software without specific prior written permission. 13 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 14 * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 15 * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 16 */ 17 18 #ifndef lint 19 static char sccsid[] = "@(#)system.c 4.4 (Berkeley) 03/06/91"; 20 #endif /* not lint */ 21 22 #include <sys/types.h> 23 24 #if defined(pyr) 25 #define fd_set fdset_t 26 #endif /* defined(pyr) */ 27 28 /* 29 * Wouldn't it be nice if these REALLY were in <sys/inode.h>? Or, 30 * equivalently, if <sys/inode.h> REALLY existed? 31 */ 32 #define IREAD 00400 33 #define IWRITE 00200 34 35 #include <sys/file.h> 36 #include <sys/time.h> 37 #include <sys/socket.h> 38 #include <netinet/in.h> 39 #include <sys/wait.h> 40 41 #include <errno.h> 42 extern int errno; 43 44 #include <netdb.h> 45 #include <signal.h> 46 #include <stdio.h> 47 #include <string.h> 48 #include <pwd.h> 49 50 #include "../general/general.h" 51 #include "../ctlr/api.h" 52 #include "../api/api_exch.h" 53 54 #include "../general/globals.h" 55 56 #ifndef FD_SETSIZE 57 /* 58 * The following is defined just in case someone should want to run 59 * this telnet on a 4.2 system. 60 * 61 */ 62 63 #define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n))) 64 #define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n))) 65 #define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n))) 66 #define FD_ZERO(p) ((p)->fds_bits[0] = 0) 67 68 #endif 69 70 static int shell_pid = 0; 71 static char key[50]; /* Actual key */ 72 static char *keyname; /* Name of file with key in it */ 73 74 static char *ourENVlist[200]; /* Lots of room */ 75 76 static int 77 sock = -1, /* Connected socket */ 78 serversock; /* Server (listening) socket */ 79 80 static enum { DEAD, UNCONNECTED, CONNECTED } state; 81 82 static long 83 storage_location; /* Address we have */ 84 static short 85 storage_length = 0; /* Length we have */ 86 static int 87 storage_must_send = 0, /* Storage belongs on other side of wire */ 88 storage_accessed = 0; /* The storage is accessed (so leave alone)! */ 89 90 static long storage[1000]; 91 92 static union REGS inputRegs; 93 static struct SREGS inputSregs; 94 95 extern int apitrace; 96 97 static void 98 kill_connection() 99 { 100 state = UNCONNECTED; 101 if (sock != -1) { 102 (void) close(sock); 103 sock = -1; 104 } 105 } 106 107 108 static int 109 nextstore() 110 { 111 struct storage_descriptor sd; 112 113 if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) { 114 storage_length = 0; 115 return -1; 116 } 117 storage_length = sd.length; 118 storage_location = sd.location; 119 if (storage_length > sizeof storage) { 120 fprintf(stderr, "API client tried to send too much storage (%d).\n", 121 storage_length); 122 storage_length = 0; 123 return -1; 124 } 125 if (api_exch_intype(EXCH_TYPE_BYTES, storage_length, (char *)storage) 126 == -1) { 127 storage_length = 0; 128 return -1; 129 } 130 return 0; 131 } 132 133 134 static int 135 doreject(message) 136 char *message; 137 { 138 struct storage_descriptor sd; 139 int length = strlen(message); 140 141 if (api_exch_outcommand(EXCH_CMD_REJECTED) == -1) { 142 return -1; 143 } 144 sd.length = length; 145 if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) { 146 return -1; 147 } 148 if (api_exch_outtype(EXCH_TYPE_BYTES, length, message) == -1) { 149 return -1; 150 } 151 return 0; 152 } 153 154 155 /* 156 * doassociate() 157 * 158 * Negotiate with the other side and try to do something. 159 * 160 * Returns: 161 * 162 * -1: Error in processing 163 * 0: Invalid password entered 164 * 1: Association OK 165 */ 166 167 static int 168 doassociate() 169 { 170 struct passwd *pwent; 171 char 172 promptbuf[100], 173 buffer[200]; 174 struct storage_descriptor sd; 175 extern char *crypt(); 176 177 if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) { 178 return -1; 179 } 180 sd.length = sd.length; 181 if (sd.length > sizeof buffer) { 182 doreject("(internal error) Authentication key too long"); 183 return -1; 184 } 185 if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) { 186 return -1; 187 } 188 buffer[sd.length] = 0; 189 190 if (strcmp(buffer, key) != 0) { 191 #if (!defined(sun)) || defined(BSD) && (BSD >= 43) 192 extern uid_t geteuid(); 193 #endif /* (!defined(sun)) || defined(BSD) && (BSD >= 43) */ 194 195 if ((pwent = getpwuid((int)geteuid())) == 0) { 196 return -1; 197 } 198 sprintf(promptbuf, "Enter password for user %s:", pwent->pw_name); 199 if (api_exch_outcommand(EXCH_CMD_SEND_AUTH) == -1) { 200 return -1; 201 } 202 sd.length = strlen(promptbuf); 203 if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) 204 == -1) { 205 return -1; 206 } 207 if (api_exch_outtype(EXCH_TYPE_BYTES, strlen(promptbuf), promptbuf) 208 == -1) { 209 return -1; 210 } 211 sd.length = strlen(pwent->pw_name); 212 if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) 213 == -1) { 214 return -1; 215 } 216 if (api_exch_outtype(EXCH_TYPE_BYTES, 217 strlen(pwent->pw_name), pwent->pw_name) == -1) { 218 return -1; 219 } 220 if (api_exch_incommand(EXCH_CMD_AUTH) == -1) { 221 return -1; 222 } 223 if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) 224 == -1) { 225 return -1; 226 } 227 sd.length = sd.length; 228 if (sd.length > sizeof buffer) { 229 doreject("Password entered was too long"); 230 return -1; 231 } 232 if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) { 233 return -1; 234 } 235 buffer[sd.length] = 0; 236 237 /* Is this the correct password? */ 238 if (strlen(pwent->pw_name)) { 239 char *ptr; 240 int i; 241 242 ptr = pwent->pw_name; 243 i = 0; 244 while (i < sd.length) { 245 buffer[i++] ^= *ptr++; 246 if (*ptr == 0) { 247 ptr = pwent->pw_name; 248 } 249 } 250 } 251 if (strcmp(crypt(buffer, pwent->pw_passwd), pwent->pw_passwd) != 0) { 252 doreject("Invalid password"); 253 sleep(10); /* Don't let us do too many of these */ 254 return 0; 255 } 256 } 257 if (api_exch_outcommand(EXCH_CMD_ASSOCIATED) == -1) { 258 return -1; 259 } else { 260 return 1; 261 } 262 } 263 264 265 void 266 freestorage() 267 { 268 struct storage_descriptor sd; 269 270 if (storage_accessed) { 271 fprintf(stderr, "Internal error - attempt to free accessed storage.\n"); 272 fprintf(stderr, "(Encountered in file %s at line %d.)\n", 273 __FILE__, __LINE__); 274 quit(); 275 } 276 if (storage_must_send == 0) { 277 return; 278 } 279 storage_must_send = 0; 280 if (api_exch_outcommand(EXCH_CMD_HEREIS) == -1) { 281 kill_connection(); 282 return; 283 } 284 sd.length = storage_length; 285 sd.location = storage_location; 286 if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) { 287 kill_connection(); 288 return; 289 } 290 if (api_exch_outtype(EXCH_TYPE_BYTES, storage_length, (char *)storage) 291 == -1) { 292 kill_connection(); 293 return; 294 } 295 } 296 297 298 static int 299 getstorage(address, length, copyin) 300 long 301 address; 302 int 303 length, 304 copyin; 305 { 306 struct storage_descriptor sd; 307 308 freestorage(); 309 if (storage_accessed) { 310 fprintf(stderr, 311 "Internal error - attempt to get while storage accessed.\n"); 312 fprintf(stderr, "(Encountered in file %s at line %d.)\n", 313 __FILE__, __LINE__); 314 quit(); 315 } 316 storage_must_send = 0; 317 if (api_exch_outcommand(EXCH_CMD_GIMME) == -1) { 318 kill_connection(); 319 return -1; 320 } 321 storage_location = address; 322 storage_length = length; 323 if (copyin) { 324 sd.location = (long)storage_location; 325 sd.length = storage_length; 326 if (api_exch_outtype(EXCH_TYPE_STORE_DESC, 327 sizeof sd, (char *)&sd) == -1) { 328 kill_connection(); 329 return -1; 330 } 331 if (api_exch_incommand(EXCH_CMD_HEREIS) == -1) { 332 fprintf(stderr, "Bad data from other side.\n"); 333 fprintf(stderr, "(Encountered at %s, %d.)\n", __FILE__, __LINE__); 334 return -1; 335 } 336 if (nextstore() == -1) { 337 kill_connection(); 338 return -1; 339 } 340 } 341 return 0; 342 } 343 344 /*ARGSUSED*/ 345 void 346 movetous(local, es, di, length) 347 char 348 *local; 349 unsigned int 350 es, 351 di; 352 int 353 length; 354 { 355 long where = SEG_OFF_BACK(es, di); 356 357 if (length > sizeof storage) { 358 fprintf(stderr, "Internal API error - movetous() length too long.\n"); 359 fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__); 360 quit(); 361 } else if (length == 0) { 362 return; 363 } 364 getstorage(where, length, 1); 365 memcpy(local, (char *)(storage+((where-storage_location))), length); 366 if (apitrace) { 367 Dump('(', local, length); 368 } 369 } 370 371 /*ARGSUSED*/ 372 void 373 movetothem(es, di, local, length) 374 unsigned int 375 es, 376 di; 377 char 378 *local; 379 int 380 length; 381 { 382 long where = SEG_OFF_BACK(es, di); 383 384 if (length > sizeof storage) { 385 fprintf(stderr, "Internal API error - movetothem() length too long.\n"); 386 fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__); 387 quit(); 388 } else if (length == 0) { 389 return; 390 } 391 freestorage(); 392 memcpy((char *)storage, local, length); 393 if (apitrace) { 394 Dump(')', local, length); 395 } 396 storage_length = length; 397 storage_location = where; 398 storage_must_send = 1; 399 } 400 401 402 char * 403 access_api(location, length, copyin) 404 char * 405 location; 406 int 407 length, 408 copyin; /* Do we need to copy in initially? */ 409 { 410 if (storage_accessed) { 411 fprintf(stderr, "Internal error - storage accessed twice\n"); 412 fprintf(stderr, "(Encountered in file %s, line %d.)\n", 413 __FILE__, __LINE__); 414 quit(); 415 } else if (length != 0) { 416 freestorage(); 417 getstorage((long)location, length, copyin); 418 storage_accessed = 1; 419 } 420 return (char *) storage; 421 } 422 423 /*ARGSUSED*/ 424 void 425 unaccess_api(location, local, length, copyout) 426 char *location; 427 char *local; 428 int length; 429 int copyout; 430 { 431 if (storage_accessed == 0) { 432 fprintf(stderr, "Internal error - unnecessary unaccess_api call.\n"); 433 fprintf(stderr, "(Encountered in file %s, line %d.)\n", 434 __FILE__, __LINE__); 435 quit(); 436 } 437 storage_accessed = 0; 438 storage_must_send = copyout; /* if needs to go back */ 439 } 440 441 /* 442 * Accept a connection from an API client, aborting if the child dies. 443 */ 444 445 static int 446 doconnect() 447 { 448 fd_set fdset; 449 int i; 450 451 sock = -1; 452 FD_ZERO(&fdset); 453 while (shell_active && (sock == -1)) { 454 FD_SET(serversock, &fdset); 455 if ((i = select(serversock+1, &fdset, 456 (fd_set *)0, (fd_set *)0, (struct timeval *)0)) < 0) { 457 if (errno = EINTR) { 458 continue; 459 } else { 460 perror("in select waiting for API connection"); 461 return -1; 462 } 463 } else { 464 i = accept(serversock, (struct sockaddr *)0, (int *)0); 465 if (i == -1) { 466 perror("accepting API connection"); 467 return -1; 468 } 469 sock = i; 470 } 471 } 472 /* If the process has already exited, we may need to close */ 473 if ((shell_active == 0) && (sock != -1)) { 474 extern void setcommandmode(); 475 476 (void) close(sock); 477 sock = -1; 478 setcommandmode(); /* In case child_died sneaked in */ 479 } 480 return 0; 481 } 482 483 /* 484 * shell_continue() actually runs the command, and looks for API 485 * requests coming back in. 486 * 487 * We are called from the main loop in telnet.c. 488 */ 489 490 int 491 shell_continue() 492 { 493 int i; 494 495 switch (state) { 496 case DEAD: 497 pause(); /* Nothing to do */ 498 break; 499 case UNCONNECTED: 500 if (doconnect() == -1) { 501 kill_connection(); 502 return -1; 503 } 504 /* At this point, it is possible that we've gone away */ 505 if (shell_active == 0) { 506 kill_connection(); 507 return -1; 508 } 509 if (api_exch_init(sock, "server") == -1) { 510 return -1; 511 } 512 while (state == UNCONNECTED) { 513 if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) { 514 kill_connection(); 515 return -1; 516 } else { 517 switch (doassociate()) { 518 case -1: 519 kill_connection(); 520 return -1; 521 case 0: 522 break; 523 case 1: 524 state = CONNECTED; 525 } 526 } 527 } 528 break; 529 case CONNECTED: 530 switch (i = api_exch_nextcommand()) { 531 case EXCH_CMD_REQUEST: 532 if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs, 533 (char *)&inputRegs) == -1) { 534 kill_connection(); 535 } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs, 536 (char *)&inputSregs) == -1) { 537 kill_connection(); 538 } else if (nextstore() == -1) { 539 kill_connection(); 540 } else { 541 handle_api(&inputRegs, &inputSregs); 542 freestorage(); /* Send any storage back */ 543 if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) { 544 kill_connection(); 545 } else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs, 546 (char *)&inputRegs) == -1) { 547 kill_connection(); 548 } else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs, 549 (char *)&inputSregs) == -1) { 550 kill_connection(); 551 } 552 /* Done, and it all worked! */ 553 } 554 break; 555 case EXCH_CMD_DISASSOCIATE: 556 kill_connection(); 557 break; 558 default: 559 if (i != -1) { 560 fprintf(stderr, 561 "Looking for a REQUEST or DISASSOCIATE command\n"); 562 fprintf(stderr, "\treceived 0x%02x.\n", i); 563 } 564 kill_connection(); 565 break; 566 } 567 } 568 return shell_active; 569 } 570 571 572 static void 573 child_died(code) 574 { 575 union wait status; 576 register int pid; 577 578 while ((pid = wait3((int *)&status, WNOHANG, (struct rusage *)0)) > 0) { 579 if (pid == shell_pid) { 580 char inputbuffer[100]; 581 extern void setconnmode(); 582 extern void ConnectScreen(); 583 584 shell_active = 0; 585 if (sock != -1) { 586 (void) close(sock); 587 sock = -1; 588 } 589 printf("[Hit return to continue]"); 590 fflush(stdout); 591 (void) gets(inputbuffer); 592 setconnmode(); 593 ConnectScreen(); /* Turn screen on (if need be) */ 594 (void) close(serversock); 595 (void) unlink(keyname); 596 } 597 } 598 signal(SIGCHLD, child_died); 599 } 600 601 602 /* 603 * Called from telnet.c to fork a lower command.com. We 604 * use the spint... routines so that we can pick up 605 * interrupts generated by application programs. 606 */ 607 608 609 int 610 shell(argc,argv) 611 int argc; 612 char *argv[]; 613 { 614 int length; 615 struct sockaddr_in server; 616 char sockNAME[100]; 617 static char **whereAPI = 0; 618 int fd; 619 struct timeval tv; 620 long ikey; 621 extern long random(); 622 extern char *mktemp(); 623 extern char *strcpy(); 624 625 /* First, create verification file. */ 626 do { 627 keyname = mktemp(strdup("/tmp/apiXXXXXX")); 628 fd = open(keyname, O_RDWR|O_CREAT|O_EXCL, IREAD|IWRITE); 629 } while ((fd == -1) && (errno == EEXIST)); 630 631 if (fd == -1) { 632 perror("open"); 633 return 0; 634 } 635 636 /* Now, get seed for random */ 637 638 if (gettimeofday(&tv, (struct timezone *)0) == -1) { 639 perror("gettimeofday"); 640 return 0; 641 } 642 srandom(tv.tv_usec); /* seed random number generator */ 643 do { 644 ikey = random(); 645 } while (ikey == 0); 646 sprintf(key, "%lu\n", (unsigned long) ikey); 647 if (write(fd, key, strlen(key)) != strlen(key)) { 648 perror("write"); 649 return 0; 650 } 651 key[strlen(key)-1] = 0; /* Get rid of newline */ 652 653 if (close(fd) == -1) { 654 perror("close"); 655 return 0; 656 } 657 658 /* Next, create the socket which will be connected to */ 659 serversock = socket(AF_INET, SOCK_STREAM, 0); 660 if (serversock < 0) { 661 perror("opening API socket"); 662 return 0; 663 } 664 server.sin_family = AF_INET; 665 server.sin_addr.s_addr = INADDR_ANY; 666 server.sin_port = 0; 667 if (bind(serversock, (struct sockaddr *)&server, sizeof server) < 0) { 668 perror("binding API socket"); 669 return 0; 670 } 671 length = sizeof server; 672 if (getsockname(serversock, (struct sockaddr *)&server, &length) < 0) { 673 perror("getting API socket name"); 674 (void) close(serversock); 675 } 676 listen(serversock, 1); 677 /* Get name to advertise in address list */ 678 strcpy(sockNAME, "API3270="); 679 gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME)); 680 if (strlen(sockNAME) > (sizeof sockNAME-(10+strlen(keyname)))) { 681 fprintf(stderr, "Local hostname too large; using 'localhost'.\n"); 682 strcpy(sockNAME, "localhost"); 683 } 684 sprintf(sockNAME+strlen(sockNAME), ":%u", ntohs(server.sin_port)); 685 sprintf(sockNAME+strlen(sockNAME), ":%s", keyname); 686 687 if (whereAPI == 0) { 688 char **ptr, **nextenv; 689 extern char **environ; 690 691 ptr = environ; 692 nextenv = ourENVlist; 693 while (*ptr) { 694 if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) { 695 fprintf(stderr, "Too many environmental variables\n"); 696 break; 697 } 698 *nextenv++ = *ptr++; 699 } 700 whereAPI = nextenv++; 701 *nextenv++ = 0; 702 environ = ourENVlist; /* New environment */ 703 } 704 *whereAPI = sockNAME; 705 706 child_died(); /* Start up signal handler */ 707 shell_active = 1; /* We are running down below */ 708 if (shell_pid = vfork()) { 709 if (shell_pid == -1) { 710 perror("vfork"); 711 (void) close(serversock); 712 } else { 713 state = UNCONNECTED; 714 } 715 } else { /* New process */ 716 register int i; 717 718 for (i = 3; i < 30; i++) { 719 (void) close(i); 720 } 721 if (argc == 1) { /* Just get a shell */ 722 char *cmdname; 723 extern char *getenv(); 724 725 cmdname = getenv("SHELL"); 726 execlp(cmdname, cmdname, 0); 727 perror("Exec'ing new shell...\n"); 728 exit(1); 729 } else { 730 execvp(argv[1], &argv[1]); 731 perror("Exec'ing command.\n"); 732 exit(1); 733 } 734 /*NOTREACHED*/ 735 } 736 return shell_active; /* Go back to main loop */ 737 } 738