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