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