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