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