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