#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <X11/Xos.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/socket.h>
#include <assert.h>
#include <X11/X.h>
#include <X11/Xproto.h>
#include <X11/Xfuncs.h>
#include <X11/ICE/ICElib.h>
#include "xfwp.h"
#include "misc.h"
#include "pm.h"
#include "transport.h"
#include "io.h"
static char * server_reason[2] = {
"Authentication rejected",
"permission denied"
};
static void
RemoveFDFromServerListenArray (
int fd_counter,
fd_set * rinit,
int num_servers)
{
int i;
for (i = 0; i < num_servers; i++)
{
if (server_array[i] != NULL &&
server_array[i]->server_fd == fd_counter)
{
FD_CLR (server_array[i]->client_listen_fd, rinit);
(void) close (server_array[i]->client_listen_fd);
if (server_array[i]->x_server_hostport)
free ((char *) server_array[i]->x_server_hostport);
if (server_array[i]->listen_port_string)
free ((char *) server_array[i]->listen_port_string);
free ((char *) server_array[i]);
server_array[i] = NULL;
break;
}
}
}
static void
doProcessWritables(
int fd_counter,
fd_set * rinit,
fd_set * winit)
{
int bytes_written;
int remainder;
if (client_conn_array[fd_counter]->wbytes)
{
bytes_written = write (fd_counter,
client_conn_array[fd_counter]->writebuf,
client_conn_array[fd_counter]->wbytes);
if (bytes_written == -1)
{
if (errno == EPIPE)
{
(void) fprintf (stderr, "write error - EPIPE\n");
perror("socket write");
}
FD_CLR(fd_counter, rinit);
FD_CLR(fd_counter, winit);
(void) close (fd_counter);
if (client_conn_array[fd_counter]->conn_to != -1)
{
FD_CLR(client_conn_array[fd_counter]->conn_to, rinit);
FD_CLR(client_conn_array[fd_counter]->conn_to, winit);
(void) close (client_conn_array[fd_counter]->conn_to);
}
client_conn_array[client_conn_array[fd_counter]->conn_to]->conn_to
= -1;
client_conn_array[fd_counter]->conn_to = -1;
if (client_conn_array[fd_counter]->source)
free(client_conn_array[fd_counter]->source);
if (client_conn_array[fd_counter]->destination)
free(client_conn_array[fd_counter]->destination);
free(client_conn_array[fd_counter]);
client_conn_array[fd_counter] = NULL;
return;
} else
{
remainder = client_conn_array[fd_counter]->wbytes - bytes_written;
if (remainder)
{
bcopy(client_conn_array[fd_counter]->writebuf + bytes_written,
client_conn_array[fd_counter]->writebuf,
remainder);
client_conn_array[fd_counter]->wbytes = remainder;
} else
client_conn_array[fd_counter]->wbytes = 0;
if ((client_conn_array[fd_counter]->conn_to != -1) &&
(client_conn_array[client_conn_array[fd_counter]->conn_to]->rbytes))
doCopyFromTo(client_conn_array[fd_counter]->conn_to,
fd_counter,
rinit,
winit);
if (client_conn_array[fd_counter]->wbytes == 0)
{
if (client_conn_array[fd_counter]->wclose == 1)
{
FD_CLR(fd_counter, rinit);
client_conn_array[fd_counter]->conn_to = -1;
client_conn_array[fd_counter]->wclose = 0;
(void) close (fd_counter);
if (client_conn_array[fd_counter]->source)
free(client_conn_array[fd_counter]->source);
if (client_conn_array[fd_counter]->destination)
free(client_conn_array[fd_counter]->destination);
free(client_conn_array[fd_counter]);
client_conn_array[fd_counter] = NULL;
}
FD_CLR(fd_counter, winit);
}
if (client_conn_array[fd_counter] != NULL)
if (client_conn_array[fd_counter]->conn_to != -1)
FD_SET(client_conn_array[fd_counter]->conn_to, rinit);
}
} else
{
if ((client_conn_array[fd_counter]->conn_to != -1) &&
(client_conn_array[client_conn_array[fd_counter]->conn_to]->rbytes))
{
doCopyFromTo (client_conn_array[fd_counter]->conn_to,
fd_counter,
rinit,
winit);
if (client_conn_array[fd_counter]->wbytes)
return;
}
if (client_conn_array[fd_counter]->wclose)
{
FD_CLR(fd_counter, rinit);
client_conn_array[fd_counter]->conn_to = -1;
client_conn_array[fd_counter]->wclose = 0;
(void) close (fd_counter);
if (client_conn_array[fd_counter]->source)
free(client_conn_array[fd_counter]->source);
if (client_conn_array[fd_counter]->destination)
free(client_conn_array[fd_counter]->destination);
free(client_conn_array[fd_counter]);
client_conn_array[fd_counter] = NULL;
}
FD_CLR(fd_counter, winit);
}
}
static void
ProcessNewPMConnection (
int * nfds,
fd_set * rinit,
struct config * config_info,
IceListenObj ** listen_objects,
int listen_fd)
{
IceConn new_ice_conn;
IceAcceptStatus accept_status;
int temp_sock_fd;
IceListenObj * temp_obj;
struct timeval time_val;
struct timezone time_zone;
struct sockaddr_in temp_sockaddr_in;
struct sockaddr_in server_sockaddr_in;
int retval;
int addrlen = sizeof(temp_sockaddr_in);
int rule_number;
int pm_idx;
for (pm_idx = 0; pm_idx < config_info->num_pm_conns; pm_idx++)
{
if (!pm_conn_array[pm_idx])
break;
}
if (pm_idx >= config_info->num_pm_conns)
{
(void) fprintf (stderr,
"Maximum number of PM connections has been reached (%d)\n",
config_info->num_pm_conns);
temp_obj = *listen_objects;
new_ice_conn = IceAcceptConnection(temp_obj[listen_fd], &accept_status);
if (new_ice_conn)
IceCloseConnection(new_ice_conn);
return;
}
temp_obj = *listen_objects;
new_ice_conn = IceAcceptConnection(temp_obj[listen_fd], &accept_status);
if (!new_ice_conn)
{
static int been_here;
if (!been_here)
been_here++;
else
(void) fprintf(stderr, "IceAcceptConnection failed (%d)\n",
accept_status);
return;
}
temp_sock_fd = IceConnectionNumber(new_ice_conn);
retval = getpeername(temp_sock_fd,
(struct sockaddr*)&temp_sockaddr_in,
(void *)&addrlen);
if (retval)
{
IceCloseConnection(new_ice_conn);
(void) fprintf(stderr, "getpeername call failed\n");
return;
}
assert(temp_sockaddr_in.sin_family == AF_INET);
if ((doConfigCheck(&temp_sockaddr_in,
&server_sockaddr_in,
config_info,
PMGR,
&rule_number)) == FAILURE)
{
(void) fprintf(stderr, "PM failed config check\n");
IceCloseConnection(new_ice_conn);
return;
}
if ((pm_conn_array[pm_idx] =
(struct pm_conn_buf *) malloc(sizeof(struct pm_conn_buf))) == NULL)
{
(void) fprintf (stderr, "malloc - PM connection object\n");
return;
}
pm_conn_array[pm_idx]->fd = temp_sock_fd;
pm_conn_array[pm_idx]->ice_conn = new_ice_conn;
FD_SET(temp_sock_fd, rinit);
*nfds = max(*nfds, temp_sock_fd + 1);
(void) gettimeofday(&time_val, &time_zone);
pm_conn_array[pm_idx]->creation_time = time_val.tv_sec;
pm_conn_array[pm_idx]->time_to_close = config_info->pm_data_timeout;
}
static void
ProcessPMInput (
int * nfds,
fd_set * rinit,
int pm_idx)
{
IceProcessMessagesStatus process_status;
switch (IceConnectionStatus(pm_conn_array[pm_idx]->ice_conn))
{
case IceConnectPending:
(void) IceProcessMessages(pm_conn_array[pm_idx]->ice_conn,
NULL, NULL);
break;
case IceConnectAccepted:
process_status = IceProcessMessages(pm_conn_array[pm_idx]->ice_conn,
NULL, NULL);
switch (process_status)
{
case IceProcessMessagesSuccess:
break;
case IceProcessMessagesIOError:
case IceProcessMessagesConnectionClosed:
if (process_status == IceProcessMessagesIOError)
IceCloseConnection(pm_conn_array[pm_idx]->ice_conn);
else
;
FD_CLR(pm_conn_array[pm_idx]->fd, rinit);
*nfds = max(*nfds, pm_conn_array[pm_idx]->fd + 1);
free(pm_conn_array[pm_idx]);
pm_conn_array[pm_idx] = NULL;
break;
default:
(void) fprintf (stderr, "IceProcessMessages error\n");
}
break;
case IceConnectRejected:
(void) fprintf (stderr, "PM IceConnectRejected\n");
break;
case IceConnectIOError:
(void) fprintf (stderr, "PM IceConnectIOError\n");
break;
default:
(void) fprintf (stderr, "IceConnectionStatus error\n");
}
}
static void
ProcessNewClientConnection (
int * nfds,
fd_set * rinit,
struct config * config_info,
int accept_fd,
int server_idx)
{
int temp_sock_fd;
struct sockaddr_in temp_sockaddr_in;
struct timeval time_val;
struct timezone time_zone;
int rule_number = -1;
int server_fd;
struct sockaddr_in server_sockaddr_in;
int temp_sock_len = sizeof(temp_sockaddr_in);
if ((temp_sock_fd = accept(accept_fd,
(struct sockaddr *) &temp_sockaddr_in,
(void *)&temp_sock_len)) < 0)
{
(void) fprintf (stderr, "accept call for a client failed\n");
return;
}
if ((doServerConnectSetup(server_array[server_idx]->x_server_hostport,
&server_array[server_idx]->server_fd,
&server_sockaddr_in)) == FAILURE)
{
(void) close (temp_sock_fd);
return;
}
if ((doServerConnect(&server_array[server_idx]->server_fd,
&server_sockaddr_in)) == FAILURE)
{
(void) close (temp_sock_fd);
(void) close (server_array[server_idx]->server_fd);
return;
}
server_fd = server_array[server_idx]->server_fd;
if ((doConfigCheck(&temp_sockaddr_in,
&server_sockaddr_in,
config_info,
CLIENT,
&rule_number)) == FAILURE)
{
doWriteLogEntry (inet_ntoa(temp_sockaddr_in.sin_addr),
inet_ntoa(server_sockaddr_in.sin_addr),
CLIENT_REJECT_CONFIG,
rule_number,
config_info);
(void) close (temp_sock_fd);
(void) close (server_fd);
return;
}
if ((client_conn_array[temp_sock_fd] = (struct client_conn_buf *)
malloc(sizeof (struct client_conn_buf))) == NULL)
{
(void) fprintf (stderr, "malloc - client connection buffer\n");
return;
}
bzero (client_conn_array[temp_sock_fd], sizeof (struct client_conn_buf));
client_conn_array[temp_sock_fd]->source =
Malloc(strlen(inet_ntoa(temp_sockaddr_in.sin_addr)) + 1);
client_conn_array[temp_sock_fd]->destination =
Malloc(strlen(inet_ntoa(server_sockaddr_in.sin_addr)) + 1);
(void) strcpy(client_conn_array[temp_sock_fd]->source,
inet_ntoa(temp_sockaddr_in.sin_addr));
(void) strcpy(client_conn_array[temp_sock_fd]->destination,
inet_ntoa(server_sockaddr_in.sin_addr));
if ((client_conn_array[server_fd] = (struct client_conn_buf *)
malloc(sizeof (struct client_conn_buf))) == NULL)
{
(void) fprintf (stderr, "malloc - server connectioin buffer\n");
return;
}
bzero (client_conn_array[server_fd], sizeof (struct client_conn_buf));
client_conn_array[server_fd]->conn_to = temp_sock_fd;
client_conn_array[temp_sock_fd]->conn_to = server_fd;
client_conn_array[temp_sock_fd]->fd = temp_sock_fd;
client_conn_array[temp_sock_fd]->state = CLIENT_WAITING;
client_conn_array[server_fd]->state = SERVER_REPLY;
FD_SET(temp_sock_fd, rinit);
*nfds = max(*nfds, temp_sock_fd + 1);
FD_SET(server_fd, rinit);
*nfds = max(*nfds, server_fd + 1);
gettimeofday(&time_val, &time_zone);
client_conn_array[temp_sock_fd]->creation_time = time_val.tv_sec;
client_conn_array[temp_sock_fd]->time_to_close =
config_info->client_data_timeout;
client_conn_array[server_fd]->creation_time = time_val.tv_sec;
client_conn_array[server_fd]->time_to_close =
config_info->client_data_timeout;
client_conn_array[server_fd]->fd = server_fd;
}
static void
ProcessClientWaiting (
fd_set * winit,
int client_idx)
{
char * conn_auth_name = "XC-QUERY-SECURITY-1";
int conn_auth_namelen;
int conn_auth_datalen;
xConnClientPrefix client;
int endian;
int name_remainder;
int data_remainder;
int idx;
char *bufP;
conn_auth_namelen = strlen(conn_auth_name);
if (SitePolicyCount == 0)
conn_auth_datalen = 0;
else
{
int sitePolicy;
conn_auth_datalen = 3;
for (sitePolicy = 0; sitePolicy < SitePolicyCount; sitePolicy++)
{
conn_auth_datalen += 1 + strlen(SitePolicies[sitePolicy]);
}
}
endian = 1;
if (*(char *) &endian)
client.byteOrder = '\154';
else
client.byteOrder = '\102';
client.majorVersion = X_PROTOCOL;
client.minorVersion = X_PROTOCOL_REVISION;
client.nbytesAuthProto = conn_auth_namelen;
client.nbytesAuthString = conn_auth_datalen;
name_remainder = (4 - (conn_auth_namelen % 4)) % 4;
data_remainder = (4 - (conn_auth_datalen % 4)) % 4;
idx = client_conn_array[client_idx]->conn_to;
bufP = client_conn_array[idx]->writebuf;
memcpy(bufP, (char *) &client, sizeof(client));
bufP += sizeof(client);
memcpy(bufP, (char *) conn_auth_name, conn_auth_namelen);
bufP += conn_auth_namelen;
bzero(bufP, name_remainder);
bufP += name_remainder;
if (conn_auth_datalen)
{
int sitePolicy;
*bufP++ = 0x02;
*bufP++ = (SitePolicyPermit == False);
*bufP++ = SitePolicyCount;
for (sitePolicy = 0; sitePolicy < SitePolicyCount; sitePolicy++)
{
char nameLen = strlen(SitePolicies[sitePolicy]);
*bufP++ = nameLen;
memcpy(bufP, SitePolicies[sitePolicy], nameLen);
bufP += nameLen;
}
bzero(bufP, data_remainder);
}
client_conn_array[idx]->wbytes = sizeof(client) +
conn_auth_namelen + name_remainder +
conn_auth_datalen + data_remainder;
FD_SET(client_conn_array[client_idx]->conn_to, winit);
client_conn_array[client_idx]->state = SERVER_WAITING;
}
static void
ProcessConnectionReady (
fd_set * rinit,
fd_set * winit,
struct config * config_info,
int client_fd)
{
int bytes_read;
if (client_conn_array[client_fd]->rbytes < RWBUFFER_SIZE)
{
bytes_read = read(client_fd,
client_conn_array[client_fd]->readbuf +
client_conn_array[client_fd]->rbytes, RWBUFFER_SIZE -
client_conn_array[client_fd]->rbytes);
if (bytes_read == -1)
{
FD_CLR(client_fd, rinit);
FD_CLR(client_fd, winit);
FD_CLR(client_conn_array[client_fd]->conn_to, rinit);
FD_CLR(client_conn_array[client_fd]->conn_to, winit);
(void) close (client_conn_array[client_fd]->conn_to);
(void) close (client_fd);
if (client_conn_array[client_fd]->source)
free(client_conn_array[client_fd]->source);
if (client_conn_array[client_fd]->destination)
free(client_conn_array[client_fd]->destination);
free(client_conn_array[client_fd]);
client_conn_array[client_fd] = NULL;
RemoveFDFromServerListenArray (client_fd,
rinit,
config_info->num_servers);
return;
} else if (bytes_read == 0)
{
FD_CLR(client_fd, rinit);
FD_CLR(client_fd, winit);
(void) close (client_fd);
if (client_conn_array[client_fd]->conn_to != -1)
{
int idx = client_conn_array[client_fd]->conn_to;
client_conn_array[idx]->wclose = 1;
client_conn_array[idx]->conn_to = -1;
FD_SET(client_conn_array[client_fd]->conn_to, winit);
}
client_conn_array[client_fd]->rbytes = 0;
client_conn_array[client_fd]->wbytes = 0;
client_conn_array[client_fd]->conn_to = -1;
RemoveFDFromServerListenArray (client_fd,
rinit,
config_info->num_servers);
} else
{
client_conn_array[client_fd]->rbytes += bytes_read;
if (client_conn_array[client_fd]->conn_to != 0)
doCopyFromTo(client_fd,
client_conn_array[client_fd]->conn_to,
rinit,
winit);
if (client_conn_array[client_fd]->rbytes >= RWBUFFER_SIZE)
{
FD_CLR(client_fd, rinit);
}
}
}
}
static void
ProcessServerReply (
fd_set * rinit,
fd_set * winit,
struct config * config_info,
int client_fd)
{
int idx;
int endian;
int four = 4;
int server_reason_remainder;
char * server_reason_padded;
int server_reason_len;
char throw_away[RWBUFFER_SIZE];
xConnSetupPrefix prefix;
if (client_conn_array[client_fd]->state == SERVER_REPLY)
{
(void) read(client_fd,
client_conn_array[client_fd]->readbuf +
client_conn_array[client_fd]->rbytes,
RWBUFFER_SIZE - client_conn_array[client_fd]->rbytes);
switch ((BYTE) client_conn_array[client_fd]->readbuf[0])
{
case SERVER_REPLY_FAILURE:
#ifdef DEBUG
{
char * replyP = client_conn_array[client_fd]->readbuf;
int reasonLength = *++replyP;
replyP += 6;
*(replyP+reasonLength+1) = '\0';
(void) fprintf (stderr, "Server replied FAILURE: %s\n", replyP);
}
#endif
case SERVER_REPLY_SUCCESS:
(void) read(client_conn_array[client_fd]->conn_to,
throw_away, 1);
prefix.success = 0;
prefix.lengthReason = server_reason_len =
strlen(server_reason
[(int) client_conn_array[client_fd]->readbuf[0]]);
prefix.majorVersion = X_PROTOCOL;
prefix.minorVersion = X_PROTOCOL_REVISION;
server_reason_remainder = server_reason_len;
while (server_reason_remainder > 0)
server_reason_remainder = server_reason_remainder - four;
server_reason_remainder = abs(server_reason_remainder);
if ((server_reason_padded =
(char *) malloc (server_reason_len +
server_reason_remainder)) == NULL)
{
(void) fprintf (stderr, "malloc - server reason\n");
return;
}
prefix.length = (server_reason_len + server_reason_remainder) /
four;
endian = 1;
if (((throw_away[0] == '\154') && !(*(char *) &endian)) ||
((throw_away[0] == '\102') && (*(char *) &endian)))
{
swab((char *) &prefix.majorVersion,
(char *) &prefix.majorVersion,
sizeof(prefix.majorVersion));
swab((char *) &prefix.minorVersion,
(char *) &prefix.minorVersion,
sizeof(prefix.minorVersion));
swab((char *) &prefix.length,
(char *) &prefix.length,
sizeof(prefix.length));
}
bzero((char *) server_reason_padded,
server_reason_len + server_reason_remainder);
memcpy((char *) server_reason_padded,
(char *) server_reason
[(int) client_conn_array[client_fd]->readbuf[0]],
server_reason_len);
memcpy((char *) client_conn_array[client_fd]->readbuf,
(char *) &prefix,
sizeof(prefix));
memcpy((char *) client_conn_array[client_fd]->readbuf +
sizeof(prefix),
(char *) server_reason_padded,
server_reason_len + server_reason_remainder);
client_conn_array[client_fd]->rbytes = sizeof(prefix) +
server_reason_len + server_reason_remainder;
idx = client_conn_array[client_fd]->conn_to;
client_conn_array[idx]->wbytes = 0;
doCopyFromTo(client_fd, idx, rinit, winit);
client_conn_array[client_fd]->wclose = 1;
client_conn_array[idx]->conn_to = -1;
FD_CLR(client_conn_array[client_fd]->conn_to, rinit);
FD_CLR(client_fd, rinit);
#ifdef DEBUG
if (((int) client_conn_array[client_fd]->readbuf[0]) ==
SERVER_REPLY_SUCCESS)
(void) fprintf (stderr, "Server replied SUCCESS\n");
#endif
free(server_reason_padded);
doWriteLogEntry (client_conn_array[idx]->source,
client_conn_array[idx]->destination,
CLIENT_REJECT_SERVER,
-1,
config_info);
break;
case SERVER_REPLY_AUTHENTICATE:
idx = client_conn_array[client_fd]->conn_to;
client_conn_array[client_fd]->state = CONNECTION_READY;
client_conn_array[idx]->state = CONNECTION_READY;
#ifdef DEBUG
(void) fprintf (stderr, "Server replied AUTHENTICATE\n");
#endif
doWriteLogEntry (client_conn_array[idx]->source,
client_conn_array[idx]->destination,
CLIENT_ACCEPT,
-1,
config_info);
break;
default:
(void) fprintf (stderr, "unknown reply from server\n");
}
}
}
static void
doProcessReadables(
int fd_counter,
int * nfds,
fd_set * rinit,
fd_set * winit,
int pm_listen_array[],
struct config * config_info,
IceListenObj ** listen_objects)
{
int i;
for (i = 0; i < config_info->num_pm_listen_ports; i++)
{
if (pm_listen_array[i] == fd_counter)
{
if (!pm_conn_array[fd_counter])
{
ProcessNewPMConnection (nfds,
rinit,
config_info,
listen_objects,
i);
return;
}
break;
}
}
for (i = 0; i < config_info->num_pm_conns; i++)
{
if (pm_conn_array[i] &&
pm_conn_array[i]->fd == fd_counter)
{
ProcessPMInput (nfds, rinit, i);
return;
}
}
for (i = 0; i < config_info->num_servers; i++)
{
if (server_array[i] &&
server_array[i]->client_listen_fd == fd_counter)
{
ProcessNewClientConnection (nfds,
rinit,
config_info,
fd_counter,
i);
return;
}
}
if (!client_conn_array[fd_counter])
{
(void) fprintf (stderr, "input processing error\n");
return;
}
switch (client_conn_array[fd_counter]->state)
{
case CLIENT_WAITING:
ProcessClientWaiting (winit,
fd_counter);
break;
case CONNECTION_READY:
ProcessConnectionReady (rinit,
winit,
config_info,
fd_counter);
break;
case SERVER_WAITING:
break;
case SERVER_REPLY:
ProcessServerReply (rinit,
winit,
config_info,
fd_counter);
break;
default:
(void) fprintf (stderr, "client state error\n");
}
}
void
doProcessSelect(
int * nfds,
int * nready,
fd_set * readable,
fd_set * writable,
fd_set * rinit,
fd_set * winit,
int pm_listen_array[],
struct config * config_info,
IceListenObj ** listen_objects)
{
int fd_counter;
for (fd_counter = 0; fd_counter < *nfds && *nready; fd_counter++)
{
if (FD_ISSET(fd_counter, writable))
{
*nready -= 1;
doProcessWritables (fd_counter,
rinit,
winit);
}
if (FD_ISSET(fd_counter, readable))
{
*nready -= 1;
doProcessReadables (fd_counter,
nfds,
rinit,
winit,
pm_listen_array,
config_info,
listen_objects);
}
}
}