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	3.5 (Berkeley) 09/14/88";
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 #if	!defined(sun) && !defined(pyr)
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 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 	if (api_exch_init(sock, "server") == -1) {
497 	    return -1;
498 	}
499 	while (state == UNCONNECTED) {
500 	    if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) {
501 		kill_connection();
502 		return -1;
503 	    } else {
504 		switch (doassociate()) {
505 		case -1:
506 		    kill_connection();
507 		    return -1;
508 		case 0:
509 		    break;
510 		case 1:
511 		    state = CONNECTED;
512 		}
513 	    }
514 	}
515 	break;
516     case CONNECTED:
517 	switch (i = api_exch_nextcommand()) {
518 	case EXCH_CMD_REQUEST:
519 	    if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs,
520 				    (char *)&inputRegs) == -1) {
521 		kill_connection();
522 	    } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs,
523 				    (char *)&inputSregs) == -1) {
524 		kill_connection();
525 	    } else if (nextstore() == -1) {
526 		kill_connection();
527 	    } else {
528 		handle_api(&inputRegs, &inputSregs);
529 		freestorage();			/* Send any storage back */
530 		if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) {
531 		    kill_connection();
532 		} else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs,
533 				    (char *)&inputRegs) == -1) {
534 		    kill_connection();
535 		} else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs,
536 				    (char *)&inputSregs) == -1) {
537 		    kill_connection();
538 		}
539 		/* Done, and it all worked! */
540 	    }
541 	    break;
542 	case EXCH_CMD_DISASSOCIATE:
543 	    kill_connection();
544 	    break;
545 	default:
546 	    if (i != -1) {
547 		fprintf(stderr,
548 			"Looking for a REQUEST or DISASSOCIATE command\n");
549 		fprintf(stderr, "\treceived 0x%02x.\n", i);
550 	    }
551 	    kill_connection();
552 	    break;
553 	}
554     }
555     return shell_active;
556 }
557 
558 
559 static int
560 child_died()
561 {
562     union wait status;
563     register int pid;
564 
565     while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0) {
566 	if (pid == shell_pid) {
567 	    char inputbuffer[100];
568 	    extern void setconnmode();
569 	    extern void ConnectScreen();
570 
571 	    shell_active = 0;
572 	    if (sock != -1) {
573 		(void) close(sock);
574 		sock = -1;
575 	    }
576 	    printf("[Hit return to continue]");
577 	    fflush(stdout);
578 	    (void) gets(inputbuffer);
579 	    setconnmode();
580 	    ConnectScreen();	/* Turn screen on (if need be) */
581 	    (void) close(serversock);
582 	    (void) unlink(keyname);
583 	}
584     }
585     signal(SIGCHLD, child_died);
586 }
587 
588 
589 /*
590  * Called from telnet.c to fork a lower command.com.  We
591  * use the spint... routines so that we can pick up
592  * interrupts generated by application programs.
593  */
594 
595 
596 int
597 shell(argc,argv)
598 int	argc;
599 char	*argv[];
600 {
601     int length;
602     struct sockaddr_in server;
603     char sockNAME[100];
604     static char **whereAPI = 0;
605     int fd;
606     struct timeval tv;
607     long ikey;
608     extern long random();
609     extern char *mktemp();
610     extern char *strcpy();
611 
612     /* First, create verification file. */
613     do {
614 	keyname = mktemp("/tmp/apiXXXXXX");
615 	fd = open(keyname, O_RDWR|O_CREAT|O_EXCL, IREAD|IWRITE);
616     } while ((fd == -1) && (errno == EEXIST));
617 
618     if (fd == -1) {
619 	perror("open");
620 	return 0;
621     }
622 
623     /* Now, get seed for random */
624 
625     if (gettimeofday(&tv, (struct timezone *)0) == -1) {
626 	perror("gettimeofday");
627 	return 0;
628     }
629     srandom(tv.tv_usec);		/* seed random number generator */
630     do {
631 	ikey = random();
632     } while (ikey == 0);
633     sprintf(key, "%lu\n", (unsigned long) ikey);
634     if (write(fd, key, strlen(key)) != strlen(key)) {
635 	perror("write");
636 	return 0;
637     }
638     key[strlen(key)-1] = 0;		/* Get rid of newline */
639 
640     if (close(fd) == -1) {
641 	perror("close");
642 	return 0;
643     }
644 
645     /* Next, create the socket which will be connected to */
646     serversock = socket(AF_INET, SOCK_STREAM, 0);
647     if (serversock < 0) {
648 	perror("opening API socket");
649 	return 0;
650     }
651     server.sin_family = AF_INET;
652     server.sin_addr.s_addr = INADDR_ANY;
653     server.sin_port = 0;
654     if (bind(serversock, (struct sockaddr *)&server, sizeof server) < 0) {
655 	perror("binding API socket");
656 	return 0;
657     }
658     length = sizeof server;
659     if (getsockname(serversock, (struct sockaddr *)&server, &length) < 0) {
660 	perror("getting API socket name");
661 	(void) close(serversock);
662     }
663     listen(serversock, 1);
664     /* Get name to advertise in address list */
665     strcpy(sockNAME, "API3270=");
666     gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME));
667     if (strlen(sockNAME) > (sizeof sockNAME-(10+strlen(keyname)))) {
668 	fprintf(stderr, "Local hostname too large; using 'localhost'.\n");
669 	strcpy(sockNAME, "localhost");
670     }
671     sprintf(sockNAME+strlen(sockNAME), ":%u", ntohs(server.sin_port));
672     sprintf(sockNAME+strlen(sockNAME), ":%s", keyname);
673 
674     if (whereAPI == 0) {
675 	char **ptr, **nextenv;
676 	extern char **environ;
677 
678 	ptr = environ;
679 	nextenv = ourENVlist;
680 	while (*ptr) {
681 	    if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) {
682 		fprintf(stderr, "Too many environmental variables\n");
683 		break;
684 	    }
685 	    *nextenv++ = *ptr++;
686 	}
687 	whereAPI = nextenv++;
688 	*nextenv++ = 0;
689 	environ = ourENVlist;		/* New environment */
690     }
691     *whereAPI = sockNAME;
692 
693     child_died();			/* Start up signal handler */
694     shell_active = 1;			/* We are running down below */
695     if (shell_pid = vfork()) {
696 	if (shell_pid == -1) {
697 	    perror("vfork");
698 	    (void) close(serversock);
699 	} else {
700 	    state = UNCONNECTED;
701 	}
702     } else {				/* New process */
703 	register int i;
704 
705 	for (i = 3; i < 30; i++) {
706 	    (void) close(i);
707 	}
708 	if (argc == 1) {		/* Just get a shell */
709 	    char *cmdname;
710 	    extern char *getenv();
711 
712 	    cmdname = getenv("SHELL");
713 	    execlp(cmdname, cmdname, 0);
714 	    perror("Exec'ing new shell...\n");
715 	    exit(1);
716 	} else {
717 	    execvp(argv[1], &argv[1]);
718 	    perror("Exec'ing command.\n");
719 	    exit(1);
720 	}
721 	/*NOTREACHED*/
722     }
723     return shell_active;		/* Go back to main loop */
724 }
725