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 (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, "(Encountered in file %s at line %d.)\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, copyin)
224 int
225     address,
226     length,
227     copyin;
228 {
229     struct storage_descriptor sd;
230     char buffer[40];
231 
232     freestorage();
233     if (storage_accessed) {
234 	fprintf(stderr,
235 		"Internal error - attempt to get while storage accessed.\n");
236 	fprintf(stderr, "(Encountered in file %s at line %d.)\n",
237 			__FILE__, __LINE__);
238 	quit();
239     }
240     storage_must_send = 0;
241     if (api_exch_outcommand(EXCH_CMD_GIMME) == -1) {
242 	kill_connection();
243 	return -1;
244     }
245     storage_location = address;
246     storage_length = length;
247     if (copyin) {
248 	sd.location = htonl(storage_location);
249 	sd.length = htons(storage_length);
250 	if (api_exch_outtype(EXCH_TYPE_STORE_DESC,
251 					sizeof sd, (char *)&sd) == -1) {
252 	    kill_connection();
253 	    return -1;
254 	}
255 	if (api_exch_incommand(EXCH_CMD_HEREIS) == -1) {
256 	    fprintf(stderr, "Bad data from other side.\n");
257 	    fprintf(stderr, "(Encountered at %s, %d.)\n", __FILE__, __LINE__);
258 	    return -1;
259 	}
260 	if (nextstore() == -1) {
261 	    kill_connection();
262 	    return -1;
263 	}
264     }
265     return 0;
266 }
267 
268 void
269 movetous(local, es, di, length)
270 char
271     *local;
272 int
273     es,
274     di;
275 int
276     length;
277 {
278     if (length > sizeof storage) {
279 	fprintf(stderr, "Internal API error - movetous() length too long.\n");
280 	fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
281 	quit();
282     } else if (length == 0) {
283 	return;
284     }
285     getstorage(di, length, 1);
286     memcpy(local, storage+(di-storage_location), length);
287 }
288 
289 void
290 movetothem(es, di, local, length)
291 int
292     es,
293     di;
294 char
295     *local;
296 int
297     length;
298 {
299     if (length > sizeof storage) {
300 	fprintf(stderr, "Internal API error - movetothem() length too long.\n");
301 	fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
302 	quit();
303     } else if (length == 0) {
304 	return;
305     }
306     freestorage();
307     memcpy((char *)storage, local, length);
308     storage_length = length;
309     storage_location = di;
310     storage_must_send = 1;
311 }
312 
313 
314 char *
315 access_api(location, length, copyin)
316 int
317     location,
318     length,
319     copyin;			/* Do we need to copy in initially? */
320 {
321     if (storage_accessed) {
322 	fprintf(stderr, "Internal error - storage accessed twice\n");
323 	fprintf(stderr, "(Encountered in file %s, line %d.)\n",
324 				__FILE__, __LINE__);
325 	quit();
326     } else if (length != 0) {
327 	freestorage();
328 	getstorage(location, length, copyin);
329 	storage_accessed = 1;
330     }
331     return (char *) storage;
332 }
333 
334 unaccess_api(location, local, length, copyout)
335 int	location;
336 char	*local;
337 int	length;
338 {
339     if (storage_accessed == 0) {
340 	fprintf(stderr, "Internal error - unnecessary unaccess_api call.\n");
341 	fprintf(stderr, "(Encountered in file %s, line %d.)\n",
342 			__FILE__, __LINE__);
343 	quit();
344     }
345     storage_accessed = 0;
346     storage_must_send = copyout;	/* if needs to go back */
347 }
348 
349 /*
350  * Accept a connection from an API client, aborting if the child dies.
351  */
352 
353 static int
354 doconnect()
355 {
356     fd_set fdset;
357     int i;
358 
359     sock = -1;
360     FD_ZERO(&fdset);
361     while (shell_active && (sock == -1)) {
362 	FD_SET(serversock, &fdset);
363 	if ((i = select(serversock+1, &fdset, 0, 0, 0)) < 0) {
364 	    if (errno = EINTR) {
365 		continue;
366 	    } else {
367 		perror("in select waiting for API connection");
368 		return -1;
369 	    }
370 	} else {
371 	    i = accept(serversock, 0, 0);
372 	    if (i == -1) {
373 		perror("accepting API connection");
374 		return -1;
375 	    }
376 	    sock = i;
377 	}
378     }
379     /* If the process has already exited, we may need to close */
380     if ((shell_active == 0) && (sock != -1)) {
381 	(void) close(sock);
382 	sock = -1;
383 	setcommandmode();	/* In case child_died sneaked in */
384     }
385 }
386 
387 /*
388  * shell_continue() actually runs the command, and looks for API
389  * requests coming back in.
390  *
391  * We are called from the main loop in telnet.c.
392  */
393 
394 int
395 shell_continue()
396 {
397     int i;
398 
399     switch (state) {
400     case DEAD:
401 	pause();			/* Nothing to do */
402 	break;
403     case UNCONNECTED:
404 	if (doconnect() == -1) {
405 	    kill_connection();
406 	    return -1;
407 	}
408 	if (api_exch_init(sock, "server") == -1) {
409 	    return -1;
410 	}
411 	while (state == UNCONNECTED) {
412 	    if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) {
413 		kill_connection();
414 		return -1;
415 	    } else {
416 		switch (doassociate()) {
417 		case -1:
418 		    kill_connection();
419 		    return -1;
420 		case 0:
421 		    break;
422 		case 1:
423 		    state = CONNECTED;
424 		}
425 	    }
426 	}
427 	break;
428     case CONNECTED:
429 	switch (i = api_exch_nextcommand()) {
430 	case EXCH_CMD_REQUEST:
431 	    if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs,
432 				    (char *)&inputRegs) == -1) {
433 		kill_connection();
434 	    } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs,
435 				    (char *)&inputSregs) == -1) {
436 		kill_connection();
437 	    } else if (nextstore() == -1) {
438 		kill_connection();
439 	    } else {
440 		handle_api(&inputRegs, &inputSregs);
441 		freestorage();			/* Send any storage back */
442 		if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) {
443 		    kill_connection();
444 		} else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs,
445 				    (char *)&inputRegs) == -1) {
446 		    kill_connection();
447 		} else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs,
448 				    (char *)&inputSregs) == -1) {
449 		    kill_connection();
450 		}
451 		/* Done, and it all worked! */
452 	    }
453 	    break;
454 	case EXCH_CMD_DISASSOCIATE:
455 	    kill_connection();
456 	    break;
457 	default:
458 	    if (i != -1) {
459 		fprintf(stderr,
460 			"Looking for a REQUEST or DISASSOCIATE command\n");
461 		fprintf(stderr, "\treceived 0x%02x.\n", i);
462 	    }
463 	    kill_connection();
464 	    break;
465 	}
466     }
467     return shell_active;
468 }
469 
470 
471 static int
472 child_died()
473 {
474     union wait *status;
475     register int pid;
476 
477     while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
478 	if (pid == shell_pid) {
479 	    char inputbuffer[100];
480 
481 	    shell_active = 0;
482 	    if (sock != -1) {
483 		(void) close(sock);
484 		sock = -1;
485 	    }
486 	    printf("[Hit return to continue]");
487 	    fflush(stdout);
488 	    (void) gets(inputbuffer);
489 	    setconnmode();
490 	    ConnectScreen();	/* Turn screen on (if need be) */
491 	    (void) close(serversock);
492 	}
493     }
494     signal(SIGCHLD, child_died);
495 }
496 
497 
498 /*
499  * Called from telnet.c to fork a lower command.com.  We
500  * use the spint... routines so that we can pick up
501  * interrupts generated by application programs.
502  */
503 
504 
505 int
506 shell(argc,argv)
507 int	argc;
508 char	*argv[];
509 {
510     int length;
511     struct sockaddr_in server;
512     char sockNAME[100];
513     static char **whereAPI = 0;
514 
515     /* First, create the socket which will be connected to */
516     serversock = socket(AF_INET, SOCK_STREAM, 0);
517     if (serversock < 0) {
518 	perror("opening API socket");
519 	return 0;
520     }
521     server.sin_family = AF_INET;
522     server.sin_addr.s_addr = INADDR_ANY;
523     server.sin_port = 0;
524     if (bind(serversock, &server, sizeof server) < 0) {
525 	perror("binding API socket");
526 	return 0;
527     }
528     length = sizeof server;
529     if (getsockname(serversock, &server, &length) < 0) {
530 	perror("getting API socket name");
531 	(void) close(serversock);
532     }
533     listen(serversock, 1);
534     /* Get name to advertise in address list */
535     strcpy(sockNAME, "API3270=");
536     gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME));
537     if (strlen(sockNAME) > (sizeof sockNAME-10)) {
538 	fprintf(stderr, "Local hostname too large; using 'localhost'.\n");
539 	strcpy(sockNAME, "localhost");
540     }
541     sprintf(sockNAME+strlen(sockNAME), ":%d", ntohs(server.sin_port));
542 
543     if (whereAPI == 0) {
544 	char **ptr, **nextenv;
545 	extern char **environ;
546 
547 	ptr = environ;
548 	nextenv = ourENVlist;
549 	while (*ptr) {
550 	    if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) {
551 		fprintf(stderr, "Too many environmental variables\n");
552 		break;
553 	    }
554 	    *nextenv++ = *ptr++;
555 	}
556 	whereAPI = nextenv++;
557 	*nextenv++ = 0;
558 	environ = ourENVlist;		/* New environment */
559     }
560     *whereAPI = sockNAME;
561 
562     child_died();			/* Start up signal handler */
563     shell_active = 1;			/* We are running down below */
564     if (shell_pid = vfork()) {
565 	if (shell_pid == -1) {
566 	    perror("vfork");
567 	    (void) close(serversock);
568 	} else {
569 	    state = UNCONNECTED;
570 	}
571     } else {				/* New process */
572 	register int i;
573 
574 	for (i = 3; i < 30; i++) {
575 	    (void) close(i);
576 	}
577 	if (argc == 1) {		/* Just get a shell */
578 	    char *cmdname;
579 	    extern char *getenv();
580 
581 	    cmdname = getenv("SHELL");
582 	    execlp(cmdname, cmdname, 0);
583 	    perror("Exec'ing new shell...\n");
584 	    exit(1);
585 	} else {
586 	    execvp(argv[1], &argv[1]);
587 	    perror("Exec'ing command.\n");
588 	    exit(1);
589 	}
590 	/*NOTREACHED*/
591     }
592     return shell_active;		/* Go back to main loop */
593 }
594