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