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