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