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