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