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