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.4 (Berkeley) 08/28/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 	extern uid_t geteuid();
190 
191 	if ((pwent = getpwuid((int)geteuid())) == 0) {
192 	    return -1;
193 	}
194 	sprintf(promptbuf, "Enter password for user %s:", pwent->pw_name);
195 	if (api_exch_outcommand(EXCH_CMD_SEND_AUTH) == -1) {
196 	    return -1;
197 	}
198 	sd.length = strlen(promptbuf);
199 	if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
200 									== -1) {
201 	    return -1;
202 	}
203 	if (api_exch_outtype(EXCH_TYPE_BYTES, strlen(promptbuf), promptbuf)
204 									== -1) {
205 	    return -1;
206 	}
207 	sd.length = strlen(pwent->pw_name);
208 	if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
209 									== -1) {
210 	    return -1;
211 	}
212 	if (api_exch_outtype(EXCH_TYPE_BYTES,
213 			    strlen(pwent->pw_name), pwent->pw_name) == -1) {
214 	    return -1;
215 	}
216 	if (api_exch_incommand(EXCH_CMD_AUTH) == -1) {
217 	    return -1;
218 	}
219 	if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
220 									== -1) {
221 	    return -1;
222 	}
223 	sd.length = sd.length;
224 	if (sd.length > sizeof buffer) {
225 	    doreject("Password entered was too long");
226 	    return -1;
227 	}
228 	if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
229 	    return -1;
230 	}
231 	buffer[sd.length] = 0;
232 
233 	/* Is this the correct password? */
234 	if (strlen(pwent->pw_name)) {
235 	    char *ptr;
236 	    int i;
237 
238 	    ptr = pwent->pw_name;
239 	    i = 0;
240 	    while (i < sd.length) {
241 		buffer[i++] ^= *ptr++;
242 		if (*ptr == 0) {
243 		    ptr = pwent->pw_name;
244 		}
245 	    }
246 	}
247 	if (strcmp(crypt(buffer, pwent->pw_passwd), pwent->pw_passwd) != 0) {
248 	    doreject("Invalid password");
249 	    sleep(10);		/* Don't let us do too many of these */
250 	    return 0;
251 	}
252     }
253     if (api_exch_outcommand(EXCH_CMD_ASSOCIATED) == -1) {
254 	return -1;
255     } else {
256 	return 1;
257     }
258 }
259 
260 
261 void
262 freestorage()
263 {
264     struct storage_descriptor sd;
265 
266     if (storage_accessed) {
267 	fprintf(stderr, "Internal error - attempt to free accessed storage.\n");
268 	fprintf(stderr, "(Encountered in file %s at line %d.)\n",
269 			__FILE__, __LINE__);
270 	quit();
271     }
272     if (storage_must_send == 0) {
273 	return;
274     }
275     storage_must_send = 0;
276     if (api_exch_outcommand(EXCH_CMD_HEREIS) == -1) {
277 	kill_connection();
278 	return;
279     }
280     sd.length = storage_length;
281     sd.location = storage_location;
282     if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
283 	kill_connection();
284 	return;
285     }
286     if (api_exch_outtype(EXCH_TYPE_BYTES, storage_length, (char *)storage)
287 							    == -1) {
288 	kill_connection();
289 	return;
290     }
291 }
292 
293 
294 static int
295 getstorage(address, length, copyin)
296 long
297     address;
298 int
299     length,
300     copyin;
301 {
302     struct storage_descriptor sd;
303 
304     freestorage();
305     if (storage_accessed) {
306 	fprintf(stderr,
307 		"Internal error - attempt to get while storage accessed.\n");
308 	fprintf(stderr, "(Encountered in file %s at line %d.)\n",
309 			__FILE__, __LINE__);
310 	quit();
311     }
312     storage_must_send = 0;
313     if (api_exch_outcommand(EXCH_CMD_GIMME) == -1) {
314 	kill_connection();
315 	return -1;
316     }
317     storage_location = address;
318     storage_length = length;
319     if (copyin) {
320 	sd.location = (long)storage_location;
321 	sd.length = storage_length;
322 	if (api_exch_outtype(EXCH_TYPE_STORE_DESC,
323 					sizeof sd, (char *)&sd) == -1) {
324 	    kill_connection();
325 	    return -1;
326 	}
327 	if (api_exch_incommand(EXCH_CMD_HEREIS) == -1) {
328 	    fprintf(stderr, "Bad data from other side.\n");
329 	    fprintf(stderr, "(Encountered at %s, %d.)\n", __FILE__, __LINE__);
330 	    return -1;
331 	}
332 	if (nextstore() == -1) {
333 	    kill_connection();
334 	    return -1;
335 	}
336     }
337     return 0;
338 }
339 
340 /*ARGSUSED*/
341 void
342 movetous(local, es, di, length)
343 char
344     *local;
345 unsigned int
346     es,
347     di;
348 int
349     length;
350 {
351     long where = SEG_OFF_BACK(es, di);
352 
353     if (length > sizeof storage) {
354 	fprintf(stderr, "Internal API error - movetous() length too long.\n");
355 	fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
356 	quit();
357     } else if (length == 0) {
358 	return;
359     }
360     getstorage(where, length, 1);
361     memcpy(local, (char *)(storage+((where-storage_location))), length);
362 }
363 
364 /*ARGSUSED*/
365 void
366 movetothem(es, di, local, length)
367 unsigned int
368     es,
369     di;
370 char
371     *local;
372 int
373     length;
374 {
375     long where = SEG_OFF_BACK(es, di);
376 
377     if (length > sizeof storage) {
378 	fprintf(stderr, "Internal API error - movetothem() length too long.\n");
379 	fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
380 	quit();
381     } else if (length == 0) {
382 	return;
383     }
384     freestorage();
385     memcpy((char *)storage, local, length);
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 	if (api_exch_init(sock, "server") == -1) {
495 	    return -1;
496 	}
497 	while (state == UNCONNECTED) {
498 	    if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) {
499 		kill_connection();
500 		return -1;
501 	    } else {
502 		switch (doassociate()) {
503 		case -1:
504 		    kill_connection();
505 		    return -1;
506 		case 0:
507 		    break;
508 		case 1:
509 		    state = CONNECTED;
510 		}
511 	    }
512 	}
513 	break;
514     case CONNECTED:
515 	switch (i = api_exch_nextcommand()) {
516 	case EXCH_CMD_REQUEST:
517 	    if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs,
518 				    (char *)&inputRegs) == -1) {
519 		kill_connection();
520 	    } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs,
521 				    (char *)&inputSregs) == -1) {
522 		kill_connection();
523 	    } else if (nextstore() == -1) {
524 		kill_connection();
525 	    } else {
526 		handle_api(&inputRegs, &inputSregs);
527 		freestorage();			/* Send any storage back */
528 		if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) {
529 		    kill_connection();
530 		} else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs,
531 				    (char *)&inputRegs) == -1) {
532 		    kill_connection();
533 		} else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs,
534 				    (char *)&inputSregs) == -1) {
535 		    kill_connection();
536 		}
537 		/* Done, and it all worked! */
538 	    }
539 	    break;
540 	case EXCH_CMD_DISASSOCIATE:
541 	    kill_connection();
542 	    break;
543 	default:
544 	    if (i != -1) {
545 		fprintf(stderr,
546 			"Looking for a REQUEST or DISASSOCIATE command\n");
547 		fprintf(stderr, "\treceived 0x%02x.\n", i);
548 	    }
549 	    kill_connection();
550 	    break;
551 	}
552     }
553     return shell_active;
554 }
555 
556 
557 static int
558 child_died()
559 {
560     union wait status;
561     register int pid;
562 
563     while ((pid = wait3(&status, WNOHANG, (struct rusage *)0)) > 0) {
564 	if (pid == shell_pid) {
565 	    char inputbuffer[100];
566 	    extern void setconnmode();
567 	    extern void ConnectScreen();
568 
569 	    shell_active = 0;
570 	    if (sock != -1) {
571 		(void) close(sock);
572 		sock = -1;
573 	    }
574 	    printf("[Hit return to continue]");
575 	    fflush(stdout);
576 	    (void) gets(inputbuffer);
577 	    setconnmode();
578 	    ConnectScreen();	/* Turn screen on (if need be) */
579 	    (void) close(serversock);
580 	    (void) unlink(keyname);
581 	}
582     }
583     signal(SIGCHLD, child_died);
584 }
585 
586 
587 /*
588  * Called from telnet.c to fork a lower command.com.  We
589  * use the spint... routines so that we can pick up
590  * interrupts generated by application programs.
591  */
592 
593 
594 int
595 shell(argc,argv)
596 int	argc;
597 char	*argv[];
598 {
599     int length;
600     struct sockaddr_in server;
601     char sockNAME[100];
602     static char **whereAPI = 0;
603     int fd;
604     struct timeval tv;
605     long ikey;
606     extern long random();
607     extern char *mktemp();
608     extern char *strcpy();
609 
610     /* First, create verification file. */
611     do {
612 	keyname = mktemp("/tmp/apiXXXXXX");
613 	fd = open(keyname, O_RDWR|O_CREAT|O_EXCL, IREAD|IWRITE);
614     } while ((fd == -1) && (errno == EEXIST));
615 
616     if (fd == -1) {
617 	perror("open");
618 	return 0;
619     }
620 
621     /* Now, get seed for random */
622 
623     if (gettimeofday(&tv, (struct timezone *)0) == -1) {
624 	perror("gettimeofday");
625 	return 0;
626     }
627     srandom(tv.tv_usec);		/* seed random number generator */
628     do {
629 	ikey = random();
630     } while (ikey == 0);
631     sprintf(key, "%lu\n", (unsigned long) ikey);
632     if (write(fd, key, strlen(key)) != strlen(key)) {
633 	perror("write");
634 	return 0;
635     }
636     key[strlen(key)-1] = 0;		/* Get rid of newline */
637 
638     if (close(fd) == -1) {
639 	perror("close");
640 	return 0;
641     }
642 
643     /* Next, create the socket which will be connected to */
644     serversock = socket(AF_INET, SOCK_STREAM, 0);
645     if (serversock < 0) {
646 	perror("opening API socket");
647 	return 0;
648     }
649     server.sin_family = AF_INET;
650     server.sin_addr.s_addr = INADDR_ANY;
651     server.sin_port = 0;
652     if (bind(serversock, (struct sockaddr *)&server, sizeof server) < 0) {
653 	perror("binding API socket");
654 	return 0;
655     }
656     length = sizeof server;
657     if (getsockname(serversock, (struct sockaddr *)&server, &length) < 0) {
658 	perror("getting API socket name");
659 	(void) close(serversock);
660     }
661     listen(serversock, 1);
662     /* Get name to advertise in address list */
663     strcpy(sockNAME, "API3270=");
664     gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME));
665     if (strlen(sockNAME) > (sizeof sockNAME-(10+strlen(keyname)))) {
666 	fprintf(stderr, "Local hostname too large; using 'localhost'.\n");
667 	strcpy(sockNAME, "localhost");
668     }
669     sprintf(sockNAME+strlen(sockNAME), ":%u", ntohs(server.sin_port));
670     sprintf(sockNAME+strlen(sockNAME), ":%s", keyname);
671 
672     if (whereAPI == 0) {
673 	char **ptr, **nextenv;
674 	extern char **environ;
675 
676 	ptr = environ;
677 	nextenv = ourENVlist;
678 	while (*ptr) {
679 	    if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) {
680 		fprintf(stderr, "Too many environmental variables\n");
681 		break;
682 	    }
683 	    *nextenv++ = *ptr++;
684 	}
685 	whereAPI = nextenv++;
686 	*nextenv++ = 0;
687 	environ = ourENVlist;		/* New environment */
688     }
689     *whereAPI = sockNAME;
690 
691     child_died();			/* Start up signal handler */
692     shell_active = 1;			/* We are running down below */
693     if (shell_pid = vfork()) {
694 	if (shell_pid == -1) {
695 	    perror("vfork");
696 	    (void) close(serversock);
697 	} else {
698 	    state = UNCONNECTED;
699 	}
700     } else {				/* New process */
701 	register int i;
702 
703 	for (i = 3; i < 30; i++) {
704 	    (void) close(i);
705 	}
706 	if (argc == 1) {		/* Just get a shell */
707 	    char *cmdname;
708 	    extern char *getenv();
709 
710 	    cmdname = getenv("SHELL");
711 	    execlp(cmdname, cmdname, 0);
712 	    perror("Exec'ing new shell...\n");
713 	    exit(1);
714 	} else {
715 	    execvp(argv[1], &argv[1]);
716 	    perror("Exec'ing command.\n");
717 	    exit(1);
718 	}
719 	/*NOTREACHED*/
720     }
721     return shell_active;		/* Go back to main loop */
722 }
723