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.2 (Berkeley) 05/30/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 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 /* At this point, it is possible that we've gone away */ 497 if (shell_active == 0) { 498 kill_connection(); 499 return -1; 500 } 501 if (api_exch_init(sock, "server") == -1) { 502 return -1; 503 } 504 while (state == UNCONNECTED) { 505 if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) { 506 kill_connection(); 507 return -1; 508 } else { 509 switch (doassociate()) { 510 case -1: 511 kill_connection(); 512 return -1; 513 case 0: 514 break; 515 case 1: 516 state = CONNECTED; 517 } 518 } 519 } 520 break; 521 case CONNECTED: 522 switch (i = api_exch_nextcommand()) { 523 case EXCH_CMD_REQUEST: 524 if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs, 525 (char *)&inputRegs) == -1) { 526 kill_connection(); 527 } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs, 528 (char *)&inputSregs) == -1) { 529 kill_connection(); 530 } else if (nextstore() == -1) { 531 kill_connection(); 532 } else { 533 handle_api(&inputRegs, &inputSregs); 534 freestorage(); /* Send any storage back */ 535 if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) { 536 kill_connection(); 537 } else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs, 538 (char *)&inputRegs) == -1) { 539 kill_connection(); 540 } else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs, 541 (char *)&inputSregs) == -1) { 542 kill_connection(); 543 } 544 /* Done, and it all worked! */ 545 } 546 break; 547 case EXCH_CMD_DISASSOCIATE: 548 kill_connection(); 549 break; 550 default: 551 if (i != -1) { 552 fprintf(stderr, 553 "Looking for a REQUEST or DISASSOCIATE command\n"); 554 fprintf(stderr, "\treceived 0x%02x.\n", i); 555 } 556 kill_connection(); 557 break; 558 } 559 } 560 return shell_active; 561 } 562 563 564 static int 565 child_died() 566 { 567 union wait status; 568 register int pid; 569 570 while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0) { 571 if (pid == shell_pid) { 572 char inputbuffer[100]; 573 extern void setconnmode(); 574 extern void ConnectScreen(); 575 576 shell_active = 0; 577 if (sock != -1) { 578 (void) close(sock); 579 sock = -1; 580 } 581 printf("[Hit return to continue]"); 582 fflush(stdout); 583 (void) gets(inputbuffer); 584 setconnmode(); 585 ConnectScreen(); /* Turn screen on (if need be) */ 586 (void) close(serversock); 587 (void) unlink(keyname); 588 } 589 } 590 signal(SIGCHLD, child_died); 591 } 592 593 594 /* 595 * Called from telnet.c to fork a lower command.com. We 596 * use the spint... routines so that we can pick up 597 * interrupts generated by application programs. 598 */ 599 600 601 int 602 shell(argc,argv) 603 int argc; 604 char *argv[]; 605 { 606 int length; 607 struct sockaddr_in server; 608 char sockNAME[100]; 609 static char **whereAPI = 0; 610 int fd; 611 struct timeval tv; 612 long ikey; 613 extern long random(); 614 extern char *mktemp(); 615 extern char *strcpy(); 616 617 /* First, create verification file. */ 618 do { 619 keyname = mktemp("/tmp/apiXXXXXX"); 620 fd = open(keyname, O_RDWR|O_CREAT|O_EXCL, IREAD|IWRITE); 621 } while ((fd == -1) && (errno == EEXIST)); 622 623 if (fd == -1) { 624 perror("open"); 625 return 0; 626 } 627 628 /* Now, get seed for random */ 629 630 if (gettimeofday(&tv, (struct timezone *)0) == -1) { 631 perror("gettimeofday"); 632 return 0; 633 } 634 srandom(tv.tv_usec); /* seed random number generator */ 635 do { 636 ikey = random(); 637 } while (ikey == 0); 638 sprintf(key, "%lu\n", (unsigned long) ikey); 639 if (write(fd, key, strlen(key)) != strlen(key)) { 640 perror("write"); 641 return 0; 642 } 643 key[strlen(key)-1] = 0; /* Get rid of newline */ 644 645 if (close(fd) == -1) { 646 perror("close"); 647 return 0; 648 } 649 650 /* Next, create the socket which will be connected to */ 651 serversock = socket(AF_INET, SOCK_STREAM, 0); 652 if (serversock < 0) { 653 perror("opening API socket"); 654 return 0; 655 } 656 server.sin_family = AF_INET; 657 server.sin_addr.s_addr = INADDR_ANY; 658 server.sin_port = 0; 659 if (bind(serversock, (struct sockaddr *)&server, sizeof server) < 0) { 660 perror("binding API socket"); 661 return 0; 662 } 663 length = sizeof server; 664 if (getsockname(serversock, (struct sockaddr *)&server, &length) < 0) { 665 perror("getting API socket name"); 666 (void) close(serversock); 667 } 668 listen(serversock, 1); 669 /* Get name to advertise in address list */ 670 strcpy(sockNAME, "API3270="); 671 gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME)); 672 if (strlen(sockNAME) > (sizeof sockNAME-(10+strlen(keyname)))) { 673 fprintf(stderr, "Local hostname too large; using 'localhost'.\n"); 674 strcpy(sockNAME, "localhost"); 675 } 676 sprintf(sockNAME+strlen(sockNAME), ":%u", ntohs(server.sin_port)); 677 sprintf(sockNAME+strlen(sockNAME), ":%s", keyname); 678 679 if (whereAPI == 0) { 680 char **ptr, **nextenv; 681 extern char **environ; 682 683 ptr = environ; 684 nextenv = ourENVlist; 685 while (*ptr) { 686 if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) { 687 fprintf(stderr, "Too many environmental variables\n"); 688 break; 689 } 690 *nextenv++ = *ptr++; 691 } 692 whereAPI = nextenv++; 693 *nextenv++ = 0; 694 environ = ourENVlist; /* New environment */ 695 } 696 *whereAPI = sockNAME; 697 698 child_died(); /* Start up signal handler */ 699 shell_active = 1; /* We are running down below */ 700 if (shell_pid = vfork()) { 701 if (shell_pid == -1) { 702 perror("vfork"); 703 (void) close(serversock); 704 } else { 705 state = UNCONNECTED; 706 } 707 } else { /* New process */ 708 register int i; 709 710 for (i = 3; i < 30; i++) { 711 (void) close(i); 712 } 713 if (argc == 1) { /* Just get a shell */ 714 char *cmdname; 715 extern char *getenv(); 716 717 cmdname = getenv("SHELL"); 718 execlp(cmdname, cmdname, 0); 719 perror("Exec'ing new shell...\n"); 720 exit(1); 721 } else { 722 execvp(argv[1], &argv[1]); 723 perror("Exec'ing command.\n"); 724 exit(1); 725 } 726 /*NOTREACHED*/ 727 } 728 return shell_active; /* Go back to main loop */ 729 } 730