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