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