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