+/*
+ * Copyright (c) 1984-1987 by the Regents of the
+ * University of California and by Gregory Glenn Minshall.
+ *
+ * Permission to use, copy, modify, and distribute these
+ * programs and their documentation for any purpose and
+ * without fee is hereby granted, provided that this
+ * copyright and permission appear on all copies and
+ * supporting documentation, the name of the Regents of
+ * the University of California not be used in advertising
+ * or publicity pertaining to distribution of the programs
+ * without specific prior permission, and notice be given in
+ * supporting documentation that copying and distribution is
+ * by permission of the Regents of the University of California
+ * and by Gregory Glenn Minshall. Neither the Regents of the
+ * University of California nor Gregory Glenn Minshall make
+ * representations about the suitability of this software
+ * for any purpose. It is provided "as is" without
+ * express or implied warranty.
+ */
+
+#ifndef lint
+static char sccsid[] = "@(#)system.c 3.2 (Berkeley) %G%";
+#endif /* not lint */
+
+#include <sys/types.h>
+
+#if defined(pyr)
+#define fd_set fdset_t
+#endif /* defined(pyr) */
+
+#if !defined(sun) && !defined(pyr)
+#include <sys/inode.h>
+#else /* !defined(sun) */
+#define IREAD 00400
+#define IWRITE 00200
+#endif /* !defined(sun) */
+
+#include <sys/file.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
#include <sys/wait.h>
+#include <errno.h>
+extern int errno;
+
+#include <netdb.h>
+#include <signal.h>
#include <stdio.h>
+#include <pwd.h>
#include "../general/general.h"
-#include "../api/api.h"
+#include "../ctlr/api.h"
+#include "../api/api_exch.h"
#include "../general/globals.h"
+#ifndef FD_SETSIZE
+/*
+ * The following is defined just in case someone should want to run
+ * this telnet on a 4.2 system.
+ *
+ */
+
+#define FD_SET(n, p) ((p)->fds_bits[0] |= (1<<(n)))
+#define FD_CLR(n, p) ((p)->fds_bits[0] &= ~(1<<(n)))
+#define FD_ISSET(n, p) ((p)->fds_bits[0] & (1<<(n)))
+#define FD_ZERO(p) ((p)->fds_bits[0] = 0)
+#endif
+\f
static int shell_pid = 0;
+static char key[50]; /* Actual key */
+static char *keyname; /* Name of file with key in it */
+
+static char *ourENVlist[200]; /* Lots of room */
static int
-child_died()
+ sock = -1, /* Connected socket */
+ serversock; /* Server (listening) socket */
+
+static enum { DEAD, UNCONNECTED, CONNECTED } state;
+
+static int
+ storage_location, /* Address we have */
+ storage_length = 0, /* Length we have */
+ storage_must_send = 0, /* Storage belongs on other side of wire */
+ storage_accessed = 0; /* The storage is accessed (so leave alone)! */
+
+static long storage[1000];
+
+static union REGS inputRegs;
+static struct SREGS inputSregs;
+
+
+static void
+kill_connection()
{
- union wait *status;
+ state = UNCONNECTED;
+ if (sock != -1) {
+ (void) close(sock);
+ sock = -1;
+ }
+}
- while ((pid = wait3(status, WNOHANG, 0)) > 0) {
- if (pid == shell_pid) {
- shell_active = 0;
+
+static int
+nextstore()
+{
+ struct storage_descriptor sd;
+
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ storage_length = 0;
+ return -1;
+ }
+ storage_length = sd.length;
+ storage_location = sd.location;
+ if (storage_length > sizeof storage) {
+ fprintf(stderr, "API client tried to send too much storage (%d).\n",
+ storage_length);
+ storage_length = 0;
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, storage_length, (char *)storage)
+ == -1) {
+ storage_length = 0;
+ return -1;
+ }
+ return 0;
+}
+
+
+static int
+doreject(message)
+char *message;
+{
+ struct storage_descriptor sd;
+ int length = strlen(message);
+
+ if (api_exch_outcommand(EXCH_CMD_REJECTED) == -1) {
+ return -1;
+ }
+ sd.length = length;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, length, message) == -1) {
+ return -1;
+ }
+ return 0;
+}
+
+
+/*
+ * doassociate()
+ *
+ * Negotiate with the other side and try to do something.
+ *
+ * Returns:
+ *
+ * -1: Error in processing
+ * 0: Invalid password entered
+ * 1: Association OK
+ */
+
+static int
+doassociate()
+{
+ struct passwd *pwent;
+ char
+ promptbuf[100],
+ buffer[200];
+ int length;
+ int was;
+ struct storage_descriptor sd;
+
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ return -1;
+ }
+ sd.length = sd.length;
+ if (sd.length > sizeof buffer) {
+ doreject("(internal error) Authentication key too long");
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+ return -1;
+ }
+ buffer[sd.length] = 0;
+
+ if (strcmp(buffer, key) != 0) {
+ if ((pwent = getpwuid(geteuid())) == 0) {
+ return -1;
+ }
+ sprintf(promptbuf, "Enter password for user %s:", pwent->pw_name);
+ if (api_exch_outcommand(EXCH_CMD_SEND_AUTH) == -1) {
+ return -1;
+ }
+ sd.length = strlen(promptbuf);
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, strlen(promptbuf), promptbuf)
+ == -1) {
+ return -1;
+ }
+ sd.length = strlen(pwent->pw_name);
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES,
+ strlen(pwent->pw_name), pwent->pw_name) == -1) {
+ return -1;
+ }
+ if (api_exch_incommand(EXCH_CMD_AUTH) == -1) {
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd)
+ == -1) {
+ return -1;
+ }
+ sd.length = sd.length;
+ if (sd.length > sizeof buffer) {
+ doreject("Password entered was too long");
+ return -1;
+ }
+ if (api_exch_intype(EXCH_TYPE_BYTES, sd.length, buffer) == -1) {
+ return -1;
+ }
+ buffer[sd.length] = 0;
+
+ /* Is this the correct password? */
+ if (strlen(pwent->pw_name)) {
+ char *ptr;
+ int i;
+
+ ptr = pwent->pw_name;
+ i = 0;
+ while (i < sd.length) {
+ buffer[i++] ^= *ptr++;
+ if (*ptr == 0) {
+ ptr = pwent->pw_name;
+ }
+ }
+ }
+ if (strcmp(crypt(buffer, pwent->pw_passwd), pwent->pw_passwd) != 0) {
+ doreject("Invalid password");
+ sleep(10); /* Don't let us do too many of these */
+ return 0;
}
}
- signal(SIGCHLD, child_died);
+ if (api_exch_outcommand(EXCH_CMD_ASSOCIATED) == -1) {
+ return -1;
+ } else {
+ return 1;
+ }
+}
+
+
+void
+freestorage()
+{
+ char buffer[40];
+ struct storage_descriptor sd;
+
+ if (storage_accessed) {
+ fprintf(stderr, "Internal error - attempt to free accessed storage.\n");
+ fprintf(stderr, "(Encountered in file %s at line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ }
+ if (storage_must_send == 0) {
+ return;
+ }
+ storage_must_send = 0;
+ if (api_exch_outcommand(EXCH_CMD_HEREIS) == -1) {
+ kill_connection();
+ return;
+ }
+ sd.length = storage_length;
+ sd.location = storage_location;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC, sizeof sd, (char *)&sd) == -1) {
+ kill_connection();
+ return;
+ }
+ if (api_exch_outtype(EXCH_TYPE_BYTES, storage_length, (char *)storage)
+ == -1) {
+ kill_connection();
+ return;
+ }
+}
+
+
+static int
+getstorage(address, length, copyin)
+int
+ address,
+ length,
+ copyin;
+{
+ struct storage_descriptor sd;
+ char buffer[40];
+
+ freestorage();
+ if (storage_accessed) {
+ fprintf(stderr,
+ "Internal error - attempt to get while storage accessed.\n");
+ fprintf(stderr, "(Encountered in file %s at line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ }
+ storage_must_send = 0;
+ if (api_exch_outcommand(EXCH_CMD_GIMME) == -1) {
+ kill_connection();
+ return -1;
+ }
+ storage_location = address;
+ storage_length = length;
+ if (copyin) {
+ sd.location = storage_location;
+ sd.length = storage_length;
+ if (api_exch_outtype(EXCH_TYPE_STORE_DESC,
+ sizeof sd, (char *)&sd) == -1) {
+ kill_connection();
+ return -1;
+ }
+ if (api_exch_incommand(EXCH_CMD_HEREIS) == -1) {
+ fprintf(stderr, "Bad data from other side.\n");
+ fprintf(stderr, "(Encountered at %s, %d.)\n", __FILE__, __LINE__);
+ return -1;
+ }
+ if (nextstore() == -1) {
+ kill_connection();
+ return -1;
+ }
+ }
+ return 0;
+}
+
+void
+movetous(local, es, di, length)
+char
+ *local;
+int
+ es,
+ di;
+int
+ length;
+{
+ if (length > sizeof storage) {
+ fprintf(stderr, "Internal API error - movetous() length too long.\n");
+ fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
+ quit();
+ } else if (length == 0) {
+ return;
+ }
+ getstorage(di, length, 1);
+ memcpy(local, storage+(di-storage_location), length);
+}
+
+void
+movetothem(es, di, local, length)
+int
+ es,
+ di;
+char
+ *local;
+int
+ length;
+{
+ if (length > sizeof storage) {
+ fprintf(stderr, "Internal API error - movetothem() length too long.\n");
+ fprintf(stderr, "(detected in file %s, line %d)\n", __FILE__, __LINE__);
+ quit();
+ } else if (length == 0) {
+ return;
+ }
+ freestorage();
+ memcpy((char *)storage, local, length);
+ storage_length = length;
+ storage_location = di;
+ storage_must_send = 1;
+}
+
+
+char *
+access_api(location, length, copyin)
+int
+ location,
+ length,
+ copyin; /* Do we need to copy in initially? */
+{
+ if (storage_accessed) {
+ fprintf(stderr, "Internal error - storage accessed twice\n");
+ fprintf(stderr, "(Encountered in file %s, line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ } else if (length != 0) {
+ freestorage();
+ getstorage(location, length, copyin);
+ storage_accessed = 1;
+ }
+ return (char *) storage;
+}
+
+unaccess_api(location, local, length, copyout)
+int location;
+char *local;
+int length;
+{
+ if (storage_accessed == 0) {
+ fprintf(stderr, "Internal error - unnecessary unaccess_api call.\n");
+ fprintf(stderr, "(Encountered in file %s, line %d.)\n",
+ __FILE__, __LINE__);
+ quit();
+ }
+ storage_accessed = 0;
+ storage_must_send = copyout; /* if needs to go back */
+}
+
+/*
+ * Accept a connection from an API client, aborting if the child dies.
+ */
+
+static int
+doconnect()
+{
+ fd_set fdset;
+ int i;
+
+ sock = -1;
+ FD_ZERO(&fdset);
+ while (shell_active && (sock == -1)) {
+ FD_SET(serversock, &fdset);
+ if ((i = select(serversock+1, &fdset, 0, 0, 0)) < 0) {
+ if (errno = EINTR) {
+ continue;
+ } else {
+ perror("in select waiting for API connection");
+ return -1;
+ }
+ } else {
+ i = accept(serversock, 0, 0);
+ if (i == -1) {
+ perror("accepting API connection");
+ return -1;
+ }
+ sock = i;
+ }
+ }
+ /* If the process has already exited, we may need to close */
+ if ((shell_active == 0) && (sock != -1)) {
+ (void) close(sock);
+ sock = -1;
+ setcommandmode(); /* In case child_died sneaked in */
+ }
}
/*
int
shell_continue()
{
+ int i;
+
+ switch (state) {
+ case DEAD:
+ pause(); /* Nothing to do */
+ break;
+ case UNCONNECTED:
+ if (doconnect() == -1) {
+ kill_connection();
+ return -1;
+ }
+ if (api_exch_init(sock, "server") == -1) {
+ return -1;
+ }
+ while (state == UNCONNECTED) {
+ if (api_exch_incommand(EXCH_CMD_ASSOCIATE) == -1) {
+ kill_connection();
+ return -1;
+ } else {
+ switch (doassociate()) {
+ case -1:
+ kill_connection();
+ return -1;
+ case 0:
+ break;
+ case 1:
+ state = CONNECTED;
+ }
+ }
+ }
+ break;
+ case CONNECTED:
+ switch (i = api_exch_nextcommand()) {
+ case EXCH_CMD_REQUEST:
+ if (api_exch_intype(EXCH_TYPE_REGS, sizeof inputRegs,
+ (char *)&inputRegs) == -1) {
+ kill_connection();
+ } else if (api_exch_intype(EXCH_TYPE_SREGS, sizeof inputSregs,
+ (char *)&inputSregs) == -1) {
+ kill_connection();
+ } else if (nextstore() == -1) {
+ kill_connection();
+ } else {
+ handle_api(&inputRegs, &inputSregs);
+ freestorage(); /* Send any storage back */
+ if (api_exch_outcommand(EXCH_CMD_REPLY) == -1) {
+ kill_connection();
+ } else if (api_exch_outtype(EXCH_TYPE_REGS, sizeof inputRegs,
+ (char *)&inputRegs) == -1) {
+ kill_connection();
+ } else if (api_exch_outtype(EXCH_TYPE_SREGS, sizeof inputSregs,
+ (char *)&inputSregs) == -1) {
+ kill_connection();
+ }
+ /* Done, and it all worked! */
+ }
+ break;
+ case EXCH_CMD_DISASSOCIATE:
+ kill_connection();
+ break;
+ default:
+ if (i != -1) {
+ fprintf(stderr,
+ "Looking for a REQUEST or DISASSOCIATE command\n");
+ fprintf(stderr, "\treceived 0x%02x.\n", i);
+ }
+ kill_connection();
+ break;
+ }
+ }
return shell_active;
}
+static int
+child_died()
+{
+ union wait *status;
+ register int pid;
+
+ while ((pid = wait3(&status, WNOHANG, 0)) > 0) {
+ if (pid == shell_pid) {
+ char inputbuffer[100];
+
+ shell_active = 0;
+ if (sock != -1) {
+ (void) close(sock);
+ sock = -1;
+ }
+ printf("[Hit return to continue]");
+ fflush(stdout);
+ (void) gets(inputbuffer);
+ setconnmode();
+ ConnectScreen(); /* Turn screen on (if need be) */
+ (void) close(serversock);
+ (void) unlink(keyname);
+ }
+ }
+ signal(SIGCHLD, child_died);
+}
+
+
/*
* Called from telnet.c to fork a lower command.com. We
* use the spint... routines so that we can pick up
int argc;
char *argv[];
{
- if ((shell_pid = fork()) {
- shell_active = 1; /* We are running down below */
- child_killed(); /* Start up signal handler */
+ int length;
+ struct sockaddr_in server;
+ char sockNAME[100];
+ static char **whereAPI = 0;
+ int fd;
+ struct timeval tv;
+ long ikey;
+ extern long random();
+ extern char *mktemp();
+
+ /* First, create verification file. */
+ do {
+ keyname = mktemp("/tmp/apiXXXXXX");
+ fd = open(keyname, O_RDWR|O_CREAT|O_EXCL, IREAD|IWRITE);
+ } while ((fd == -1) && (errno == EEXIST));
+
+ if (fd == -1) {
+ perror("open");
+ return 0;
+ }
+
+ /* Now, get seed for random */
+
+ if (gettimeofday(&tv, 0) == -1) {
+ perror("gettimeofday");
+ return 0;
+ }
+ srandom(tv.tv_usec); /* seed random number generator */
+ do {
+ ikey = random();
+ } while (ikey == 0);
+ sprintf(key, "%lu\n", ikey);
+ if (write(fd, key, strlen(key)) != strlen(key)) {
+ perror("write");
+ return 0;
+ }
+ key[strlen(key)-1] = 0; /* Get rid of newline */
+
+ if (close(fd) == -1) {
+ perror("close");
+ return 0;
+ }
+
+ /* Next, create the socket which will be connected to */
+ serversock = socket(AF_INET, SOCK_STREAM, 0);
+ if (serversock < 0) {
+ perror("opening API socket");
+ return 0;
+ }
+ server.sin_family = AF_INET;
+ server.sin_addr.s_addr = INADDR_ANY;
+ server.sin_port = 0;
+ if (bind(serversock, &server, sizeof server) < 0) {
+ perror("binding API socket");
+ return 0;
+ }
+ length = sizeof server;
+ if (getsockname(serversock, &server, &length) < 0) {
+ perror("getting API socket name");
+ (void) close(serversock);
+ }
+ listen(serversock, 1);
+ /* Get name to advertise in address list */
+ strcpy(sockNAME, "API3270=");
+ gethostname(sockNAME+strlen(sockNAME), sizeof sockNAME-strlen(sockNAME));
+ if (strlen(sockNAME) > (sizeof sockNAME-(10+strlen(keyname)))) {
+ fprintf(stderr, "Local hostname too large; using 'localhost'.\n");
+ strcpy(sockNAME, "localhost");
+ }
+ sprintf(sockNAME+strlen(sockNAME), ":%d", ntohs(server.sin_port));
+ sprintf(sockNAME+strlen(sockNAME), ":%s", keyname);
+
+ if (whereAPI == 0) {
+ char **ptr, **nextenv;
+ extern char **environ;
+
+ ptr = environ;
+ nextenv = ourENVlist;
+ while (*ptr) {
+ if (nextenv >= &ourENVlist[highestof(ourENVlist)-1]) {
+ fprintf(stderr, "Too many environmental variables\n");
+ break;
+ }
+ *nextenv++ = *ptr++;
+ }
+ whereAPI = nextenv++;
+ *nextenv++ = 0;
+ environ = ourENVlist; /* New environment */
+ }
+ *whereAPI = sockNAME;
+
+ child_died(); /* Start up signal handler */
+ shell_active = 1; /* We are running down below */
+ if (shell_pid = vfork()) {
+ if (shell_pid == -1) {
+ perror("vfork");
+ (void) close(serversock);
+ } else {
+ state = UNCONNECTED;
+ }
} else { /* New process */
register int i;
}
if (argc == 1) { /* Just get a shell */
char *cmdname;
+ extern char *getenv();
cmdname = getenv("SHELL");
execlp(cmdname, cmdname, 0);
}
/*NOTREACHED*/
}
- return 1; /* Go back to main loop */
+ return shell_active; /* Go back to main loop */
}