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)
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, "(Encountered in file %s at line %d.)\n",
233 			__FILE__, __LINE__);
234 	quit();
235     }
236     storage_must_send = 0;
237     if (api_exch_outcommand(EXCH_CMD_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_CMD_HEREIS) == -1) {
250 	fprintf(stderr, "Bad data from other side.\n");
251 	fprintf(stderr, "(Encountered at %s, %d.)\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 %d.)\n",
316 				__FILE__, __LINE__);
317 	quit();
318     } else if (length != 0) {
319 	freestorage();
320 	getstorage(location, length);
321 	storage_accessed = 1;
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 %d.)\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     int i;
390 
391     switch (state) {
392     case DEAD:
393 	pause();			/* Nothing to do */
394 	break;
395     case UNCONNECTED:
396 	if (doconnect() == -1) {
397 	    kill_connection();
398 	    return -1;
399 	}
400 	if (api_exch_init(sock, "server") == -1) {
401 	    return -1;
402 	}
403 	while (state == UNCONNECTED) {
404 	    if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) {
405 		kill_connection();
406 		return -1;
407 	    } else {
408 		switch (doassociate()) {
409 		case -1:
410 		    kill_connection();
411 		    return -1;
412 		case 0:
413 		    break;
414 		case 1:
415 		    state = CONNECTED;
416 		}
417 	    }
418 	}
419 	break;
420     case CONNECTED:
421 	switch (i = api_exch_nextcommand()) {
422 	case EXCH_CMD_REQUEST:
423 	    if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs,
424 				    (char *)&inputRegs) == -1) {
425 		kill_connection();
426 	    } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs,
427 				    (char *)&inputSregs) == -1) {
428 		kill_connection();
429 	    } else if (nextstore() == -1) {
430 		kill_connection();
431 	    } else {
432 		handle_api(&inputRegs, &inputSregs);
433 		freestorage();			/* Send any storage back */
434 		if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) {
435 		    kill_connection();
436 		} else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs,
437 				    (char *)&inputRegs) == -1) {
438 		    kill_connection();
439 		} else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs,
440 				    (char *)&inputSregs) == -1) {
441 		    kill_connection();
442 		}
443 		/* Done, and it all worked! */
444 	    }
445 	    break;
446 	case EXCH_CMD_DISASSOCIATE:
447 	    kill_connection();
448 	    break;
449 	default:
450 	    if (i != -1) {
451 		fprintf(stderr,
452 			"Looking for a REQUEST or DISASSOCIATE command\n");
453 		fprintf(stderr, "\treceived 0x%02x.\n", i);
454 	    }
455 	    kill_connection();
456 	    break;
457 	}
458     }
459     return shell_active;
460 }
461 
462 
463 static int
464 child_died()
465 {
466     union wait *status;
467     register int pid;
468 
469     while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
470 	if (pid == shell_pid) {
471 	    char inputbuffer[100];
472 
473 	    shell_active = 0;
474 	    if (sock != -1) {
475 		(void) close(sock);
476 		sock = -1;
477 	    }
478 	    printf("[Hit return to continue]");
479 	    fflush(stdout);
480 	    (void) gets(inputbuffer);
481 	    setconnmode();
482 	    ConnectScreen();	/* Turn screen on (if need be) */
483 	    (void) close(serversock);
484 	}
485     }
486     signal(SIGCHLD, child_died);
487 }
488 
489 
490 /*
491  * Called from telnet.c to fork a lower command.com.  We
492  * use the spint... routines so that we can pick up
493  * interrupts generated by application programs.
494  */
495 
496 
497 int
498 shell(argc,argv)
499 int	argc;
500 char	*argv[];
501 {
502     int length;
503     struct sockaddr_in server;
504     char sockNAME[100];
505     static char **whereAPI = 0;
506 
507     /* First, create the socket which will be connected to */
508     serversock = socket(AF_INET, SOCK_STREAM, 0);
509     if (serversock < 0) {
510 	perror("opening API socket");
511 	return 0;
512     }
513     server.sin_family = AF_INET;
514     server.sin_addr.s_addr = INADDR_ANY;
515     server.sin_port = 0;
516     if (bind(serversock, &server, sizeof server) < 0) {
517 	perror("binding API socket");
518 	return 0;
519     }
520     length = sizeof server;
521     if (getsockname(serversock, &server, &length) < 0) {
522 	perror("getting API socket name");
523 	(void) close(serversock);
524     }
525     listen(serversock, 1);
526     /* Get name to advertise in address list */
527     strcpy(sockNAME, "API3270=");
528     gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME));
529     if (strlen(sockNAME) > (sizeof sockNAME-10)) {
530 	fprintf(stderr, "Local hostname too large; using 'localhost'.\n");
531 	strcpy(sockNAME, "localhost");
532     }
533     sprintf(sockNAME+strlen(sockNAME), ":%d", ntohs(server.sin_port));
534 
535     if (whereAPI == 0) {
536 	char **ptr, **nextenv;
537 	extern char **environ;
538 
539 	ptr = environ;
540 	nextenv = ourENVlist;
541 	while (*ptr) {
542 	    if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) {
543 		fprintf(stderr, "Too many environmental variables\n");
544 		break;
545 	    }
546 	    *nextenv++ = *ptr++;
547 	}
548 	whereAPI = nextenv++;
549 	*nextenv++ = 0;
550 	environ = ourENVlist;		/* New environment */
551     }
552     *whereAPI = sockNAME;
553 
554     child_died();			/* Start up signal handler */
555     shell_active = 1;			/* We are running down below */
556     if (shell_pid = vfork()) {
557 	if (shell_pid == -1) {
558 	    perror("vfork");
559 	    (void) close(serversock);
560 	} else {
561 	    state = UNCONNECTED;
562 	}
563     } else {				/* New process */
564 	register int i;
565 
566 	for (i = 3; i < 30; i++) {
567 	    (void) close(i);
568 	}
569 	if (argc == 1) {		/* Just get a shell */
570 	    char *cmdname;
571 	    extern char *getenv();
572 
573 	    cmdname = getenv("SHELL");
574 	    execlp(cmdname, cmdname, 0);
575 	    perror("Exec'ing new shell...\n");
576 	    exit(1);
577 	} else {
578 	    execvp(argv[1], &argv[1]);
579 	    perror("Exec'ing command.\n");
580 	    exit(1);
581 	}
582 	/*NOTREACHED*/
583     }
584     return shell_active;		/* Go back to main loop */
585 }
586