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