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