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