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