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