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