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