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