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