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