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