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