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_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_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_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_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_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_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_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 switch (state) { 390 case DEAD: 391 pause(); /* Nothing to do */ 392 break; 393 case UNCONNECTED: 394 if (doconnect() == -1) { 395 kill_connection(); 396 return -1; 397 } 398 if (api_exch_init(sock, "client") == -1) { 399 return -1; 400 } 401 while (state == UNCONNECTED) { 402 if (api_exch_incommand(EXCH_ASSOCIATE) == -1) { 403 kill_connection(); 404 return -1; 405 } else { 406 switch (doassociate()) { 407 case -1: 408 kill_connection(); 409 return -1; 410 case 0: 411 break; 412 case 1: 413 state = CONNECTED; 414 } 415 } 416 } 417 break; 418 case CONNECTED: 419 if (api_exch_incommand(EXCH_REQUEST) == -1) { 420 kill_connection(); 421 } else if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs, 422 (char *)&inputRegs) == -1) { 423 kill_connection(); 424 } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs, 425 (char *)&inputSregs) == -1) { 426 kill_connection(); 427 } else if (nextstore() == -1) { 428 kill_connection(); 429 } else { 430 handle_api(&inputRegs, &inputSregs); 431 freestorage(); /* Send any storage back */ 432 if (api_exch_outcommand(EXCH_REPLY) == -1) { 433 kill_connection(); 434 } else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs, 435 (char *)&inputRegs) == -1) { 436 kill_connection(); 437 } else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs, 438 (char *)&inputSregs) == -1) { 439 kill_connection(); 440 } 441 /* Done, and it all worked! */ 442 } 443 } 444 return shell_active; 445 } 446 447 448 static int 449 child_died() 450 { 451 union wait *status; 452 register int pid; 453 454 while ((pid = wait3(&status, WNOHANG, 0)) > 0) { 455 if (pid == shell_pid) { 456 char inputbuffer[100]; 457 458 shell_active = 0; 459 if (sock != -1) { 460 (void) close(sock); 461 sock = -1; 462 } 463 printf("[Hit return to continue]"); 464 fflush(stdout); 465 (void) gets(inputbuffer); 466 setconnmode(); 467 ConnectScreen(); /* Turn screen on (if need be) */ 468 (void) close(serversock); 469 } 470 } 471 signal(SIGCHLD, child_died); 472 } 473 474 475 /* 476 * Called from telnet.c to fork a lower command.com. We 477 * use the spint... routines so that we can pick up 478 * interrupts generated by application programs. 479 */ 480 481 482 int 483 shell(argc,argv) 484 int argc; 485 char *argv[]; 486 { 487 int length; 488 struct sockaddr_in server; 489 char sockNAME[100]; 490 static char **whereAPI = 0; 491 492 /* First, create the socket which will be connected to */ 493 serversock = socket(AF_INET, SOCK_STREAM, 0); 494 if (serversock < 0) { 495 perror("opening API socket"); 496 return 0; 497 } 498 server.sin_family = AF_INET; 499 server.sin_addr.s_addr = INADDR_ANY; 500 server.sin_port = 0; 501 if (bind(serversock, &server, sizeof server) < 0) { 502 perror("binding API socket"); 503 return 0; 504 } 505 length = sizeof server; 506 if (getsockname(serversock, &server, &length) < 0) { 507 perror("getting API socket name"); 508 (void) close(serversock); 509 } 510 listen(serversock, 1); 511 /* Get name to advertise in address list */ 512 strcpy(sockNAME, "API3270="); 513 gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME)); 514 if (strlen(sockNAME) > (sizeof sockNAME-10)) { 515 fprintf(stderr, "Local hostname too large; using 'localhost'.\n"); 516 strcpy(sockNAME, "localhost"); 517 } 518 sprintf(sockNAME+strlen(sockNAME), ":%d", ntohs(server.sin_port)); 519 520 if (whereAPI == 0) { 521 char **ptr, **nextenv; 522 extern char **environ; 523 524 ptr = environ; 525 nextenv = ourENVlist; 526 while (*ptr) { 527 if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) { 528 fprintf(stderr, "Too many environmental variables\n"); 529 break; 530 } 531 *nextenv++ = *ptr++; 532 } 533 whereAPI = nextenv++; 534 *nextenv++ = 0; 535 environ = ourENVlist; /* New environment */ 536 } 537 *whereAPI = sockNAME; 538 539 child_died(); /* Start up signal handler */ 540 shell_active = 1; /* We are running down below */ 541 if (shell_pid = vfork()) { 542 if (shell_pid == -1) { 543 perror("vfork"); 544 (void) close(serversock); 545 } else { 546 state = UNCONNECTED; 547 } 548 } else { /* New process */ 549 register int i; 550 551 for (i = 3; i < 30; i++) { 552 (void) close(i); 553 } 554 if (argc == 1) { /* Just get a shell */ 555 char *cmdname; 556 extern char *getenv(); 557 558 cmdname = getenv("SHELL"); 559 execlp(cmdname, cmdname, 0); 560 perror("Exec'ing new shell...\n"); 561 exit(1); 562 } else { 563 execvp(argv[1], &argv[1]); 564 perror("Exec'ing command.\n"); 565 exit(1); 566 } 567 /*NOTREACHED*/ 568 } 569 return shell_active; /* Go back to main loop */ 570 } 571