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