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