diff options
author | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:53 +0000 |
---|---|---|
committer | Kaleb Keithley <kaleb@freedesktop.org> | 2003-11-14 15:54:53 +0000 |
commit | 1dea1475a4beb620f350eda4ff0f902c9ec274a9 (patch) | |
tree | 08b99411d48a1351e5d7af3cd400b742a464bc3e |
R6.6 is the Xorg base-lineXORG-MAINXORG-STABLE
-rw-r--r-- | io.c | 1312 | ||||
-rw-r--r-- | io.h | 53 | ||||
-rw-r--r-- | misc.c | 1508 | ||||
-rw-r--r-- | misc.h | 111 | ||||
-rw-r--r-- | pm.c | 538 | ||||
-rw-r--r-- | pm.h | 69 | ||||
-rw-r--r-- | transport.c | 334 | ||||
-rw-r--r-- | transport.h | 70 | ||||
-rw-r--r-- | xfwp.c | 151 | ||||
-rw-r--r-- | xfwp.h | 318 | ||||
-rw-r--r-- | xfwp.man | 389 |
11 files changed, 4853 insertions, 0 deletions
@@ -0,0 +1,1312 @@ +/* $Xorg: io.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ +/* + +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <X11/Xos.h> /* Needed here for SunOS */ +#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" + +#ifdef X_NOT_STDC_ENV +extern int errno; +#endif + +/* + * Error messages returned to clients who are denied access + */ +static char * server_reason[2] = { + "Authentication rejected", + "permission denied" +}; + + +static void +RemoveFDFromServerListenArray ( + int fd_counter, + fd_set * rinit, + int num_servers) +{ + /* + * File descriptor fd_counter had a read failure. If this fd + * is associated with an Xserver, then we assume the Xserver is + * un-usable (e.g. has died) thus we should no longer listen for + * for connections on the client listen port associated with + * this Xserver. + */ + + 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; + + /* + * start off by writing from the selected fd to its connection + * partner + */ + if (client_conn_array[fd_counter]->wbytes) + { + /* + * See how much you manage to write + */ + bytes_written = write (fd_counter, + client_conn_array[fd_counter]->writebuf, + client_conn_array[fd_counter]->wbytes); + /* + * handle some common error conditions + */ + if (bytes_written == -1) + { + /* + * no process attached to the other end of this socket + */ + if (errno == EPIPE) + { + (void) fprintf (stderr, "write error - EPIPE\n"); + perror("socket write"); + } + /* + * clean up + */ + 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 + { + /* + * no errors on write, but did you write everything in the buffer? + */ + remainder = client_conn_array[fd_counter]->wbytes - bytes_written; + if (remainder) + { + /* + * move any remainder to front of writebuffer and adjust + * writebuf byte counter + */ + bcopy(client_conn_array[fd_counter]->writebuf + bytes_written, + client_conn_array[fd_counter]->writebuf, + remainder); + client_conn_array[fd_counter]->wbytes = remainder; + } else + /* + * writebuffer *must* be empty, so zero byte counter + */ + client_conn_array[fd_counter]->wbytes = 0; + + /* + * take this opportunity to get more read data, if any present + */ + 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 the above operation didn't put anything on the writebuffer, + * and the readables handler marked the fd ready to close, then + * clean up and close the connection; otherwise, simply clear the + * fd_set mask for this fd + */ + 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); + } + /* + * since we just wrote data to the conn_to fd, mark it as ready + * to check for reading when we go through select() the next time + */ + if (client_conn_array[fd_counter]->conn_to != -1) + FD_SET(client_conn_array[fd_counter]->conn_to, rinit); + } /* end else no errors on write */ + } else + { + /* + * There was nothing to write on this fd (can't see how we'd get + * here if select() returned this fd as writable, but it's in + * XForward so who am I to say?!) + */ + 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 you got anything to write, then proceed to next + * iter of select() + */ + if (client_conn_array[fd_counter]->wbytes) + return; + } + + /* + * You didn't get anything from that copy; check to see if it was + * because the readables handler marked the fd closed; if so, + * close this association; otherwise, simply clear the fd_set + * writable mask for this fd + */ + 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; + + /* + * Only continue if there is room for another PM connection. + */ + 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); + + /* + * Must accept and then close this connection or the PM will + * continue to poll. + */ + temp_obj = *listen_objects; + new_ice_conn = IceAcceptConnection(temp_obj[listen_fd], &accept_status); + if (new_ice_conn) + IceCloseConnection(new_ice_conn); + + return; + } + + /* + * accept the connection if you can, use pm_listen_array + * index to index into ICE listen_object list (this is because the + * listen_objects list must correspond to the pm_listen_array) + */ + temp_obj = *listen_objects; + new_ice_conn = IceAcceptConnection(temp_obj[listen_fd], &accept_status); + if (!new_ice_conn) + { + static int been_here; + + /* + * ICE initialization (bug?) makes this happen the + * first time readables is hit. + */ + if (!been_here) + been_here++; + else + (void) fprintf(stderr, "IceAcceptConnection failed (%d)\n", + accept_status); + return; + } + + /* + * extract the fd from this new connection; remember, the fd of + * the listen socket is *not* the fd of the actual connection! + */ + temp_sock_fd = IceConnectionNumber(new_ice_conn); + + /* + * before we get any further, do a config check on the new ICE + * connection; start by using getpeername() to get endpoint info + */ + retval = getpeername(temp_sock_fd, + (struct sockaddr*)&temp_sockaddr_in, + &addrlen); + if (retval) + { + IceCloseConnection(new_ice_conn); + (void) fprintf(stderr, "getpeername call failed\n"); + return; + } + + assert(temp_sockaddr_in.sin_family == AF_INET); + + /* + * Do a configuration check. NOTE: we're not doing anything + * with the server_sockaddr_in argument + */ + if ((doConfigCheck(&temp_sockaddr_in, + &server_sockaddr_in, + config_info, + PMGR, + &rule_number)) == FAILURE) + { + /* + * close the PM connection + * + */ + (void) fprintf(stderr, "The Proxy Manager failed the configuration check\n"); + IceCloseConnection(new_ice_conn); + return; + } + + /* + * you've started the connection process; allocate a buffer + * for this connection, then continue processing other fd's without + * blocking while waiting to read the coming PM data; [NOTE: + * we use the fd of the connection socket as index into the + * pm_conn_array; this saves us much troublesome linked-list + * management!] + */ + 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; + } + + /* + * save the ICEconn struct for future status checks; also + * the fd (although you could extract it from the ICEconn + * each time you need it, but that's a pain) + */ + pm_conn_array[pm_idx]->fd = temp_sock_fd; + pm_conn_array[pm_idx]->ice_conn = new_ice_conn; + + /* + * Set the readables select() to listen for a readable on this + * fd; remember, we're not interested in pm writables, since + * all the negotiation is handled inside this routine; adjust + * the nfds (must do that everytime we get a new socket to + * select() on), and then contnue processing current selections + */ + FD_SET(temp_sock_fd, rinit); + *nfds = max(*nfds, temp_sock_fd + 1); + + /* + * this is where we initialize the current time and timeout on this + * pm_connection object + */ + (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: + /* + * for some reason this connection still isn't ready for + * reading, so return and try next readable + */ + (void) IceProcessMessages(pm_conn_array[pm_idx]->ice_conn, + NULL, NULL); + break; + + case IceConnectAccepted: + /* + * you're ready to read the PM data, allocate and send back + * your client listen port, etc., etc.; do this inside + * FWPprocessMessages() by calling IceProcessMessages() + * [NOTE: The NULL args set it up for non-blocking] + */ + process_status = IceProcessMessages(pm_conn_array[pm_idx]->ice_conn, + NULL, NULL); + + switch (process_status) + { + case IceProcessMessagesSuccess: + + /* + * you read the server data, allocated a listen port + * for the remote client and wrote it back to the PM, + * so you don't need to do anything more until PM + * closes the connection (NOTE: Make sure we don't + * do this more than once!!) + */ + break; + + case IceProcessMessagesIOError: + IceProcessMessagesConnectionClosed: + + if (process_status == IceProcessMessagesIOError) + /* + * there was a problem with the connection, close + * it explicitly + */ + IceCloseConnection(pm_conn_array[pm_idx]->ice_conn); + else + /* + * the connection somehow closed itself, so don't call + * IceCloseConnection + */ + ; + + /* + * reset the select() readables mask and nfds, free + * the buffer memory on this array element, reset the + * pointer to NULL and return + */ + 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: + /* + * Should never get here since all of the return + * codes from IceProcessMessages have been checked. + */ + (void) fprintf (stderr, "IceProcessMessages error\n"); + } + break; + + case IceConnectRejected: + /* + * don't know yet what to do in this case, but for now simply + * output diagnostic and return to select() processing + */ + (void) fprintf (stderr, "PM IceConnectRejected\n"); + break; + + case IceConnectIOError: + /* + * don't know yet what to do in this case, but for now simply + * output diagnostic and return to select() processing + */ + (void) fprintf (stderr, "PM IceConnectIOError\n"); + break; + + default: + /* + * Should never get here since all of the return + * codes from IceConnectionStatus have been checked. + */ + (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); + + /* + * The first thing we do is accept() this connection and check it + * against configuration data to see whether its origination host + * is allowed; next, we connect to the server found in the lookup, + * synthesize a proxy connection setup request to be sent + * to that server to determine whether it`s a secure server; + * we can't block on the server reply so we go ahead and allocate + * a data structure for this client connection, and mark its + * state PENDING + */ + + if ((temp_sock_fd = accept(accept_fd, + (struct sockaddr *) &temp_sockaddr_in, + &temp_sock_len)) < 0) + { + (void) fprintf (stderr, "accept call for a client failed\n"); + return; + } + + /* + * Try to initialize and open a connection to the server. If + * an error occurs, those functions will output an appropriate + * message + */ + 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; + + /* + * do config check on client source and destination (must do + * it here because otherwise we don't have a server socket + * to query and we may not be able to resolve server name + * alone from xfindproxy() + */ + if ((doConfigCheck(&temp_sockaddr_in, + &server_sockaddr_in, + config_info, + CLIENT, + &rule_number)) == FAILURE) + { + /* + * log the client connection failure, close client and server + * sockets and return + */ + 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 configured authorization succeeds, go ahead and + * allocate a client_conn_buf struct for client connection + */ + 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)); + + /* + * save the source and destination data for this connection (since + * the log data struct will go out of scope before we check the + * server security extension or other loggable events) + */ + 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)); + + /* + * allocate a buffer for the X server connection + * and create the association between client and server + */ + 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; + + /* + * save this sock fd for future reference (in timeout computation) + */ + client_conn_array[temp_sock_fd]->fd = temp_sock_fd; + + /* + * mark this buffer as readable and writable and waiting for + * authentication to complete; mark the server conn buffer + * with a special state to make sure that its reply to + * the authentication request can be read and interpreted + * before it is simply forwarded to the client + */ + client_conn_array[temp_sock_fd]->state = CLIENT_WAITING; + client_conn_array[server_fd]->state = SERVER_REPLY; + + /* + * update the select() fd mask and the nfds + */ + FD_SET(temp_sock_fd, rinit); + *nfds = max(*nfds, temp_sock_fd + 1); + FD_SET(server_fd, rinit); + *nfds = max(*nfds, server_fd + 1); + + /* + * this is where we initialize the current time and timeout on this + * client_data object + */ + 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; + + /* + * be sure the mark the server side of the association, too + */ + 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; + + /* + * The remote client is sending more data on an already- + * established connection, but we still haven't checked + * authentication on this client from the associated + * X-server. + * + * Do the following: + * + * 1. create the authentication header + * 2. mark the server fd writable + * 3. copy the header info into the write buffer + * 3. set the wbytes field to the header size + * 4. mark the state SERVER_WAITING + */ + 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'; /* 'l' */ + else + client.byteOrder = '\102'; /* 'B' */ + + client.majorVersion = X_PROTOCOL; + client.minorVersion = X_PROTOCOL_REVISION; + client.nbytesAuthProto = conn_auth_namelen; + client.nbytesAuthString = conn_auth_datalen; + + /* + * Put the authentication message into the appropriate + * client_conn_buf object + * + * compute required padding for name and data strings + */ + 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; /* Site Policies only at this point */ + *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; + + /* + * Mark this fd as selectable to force a write() operation + * of authentication request to server for this client + */ + FD_SET(client_conn_array[client_idx]->conn_to, winit); + + /* + * Mark the connection SERVER_WAITING (so that we don't + * read any more client data until the authentication + * sequence is complete) + */ + client_conn_array[client_idx]->state = SERVER_WAITING; +} + +static void +ProcessConnectionReady ( + fd_set * rinit, + fd_set * winit, + struct config * config_info, + int client_fd) +{ + /* + * We've finished our authentication handshaking and are + * forwarding data either from client to server or vice versa + */ + int bytes_read; + + if (client_conn_array[client_fd]->rbytes < RWBUFFER_SIZE) + { + /* + * read what you have room for + */ + 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); + /* + * check for I/O error on this fd; this is our only way + * of knowing if remote closed the connection + */ + if (bytes_read == -1) + { + /* + * remote apparently closed the connection; + * clear bits in the select() mask, reclaim conn_buffs and + * listen port + */ + 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; + + /* + * If this FD is for an Xserver must remove the server's + * listen fd so that clients will not attempt to connect + * on this fd. + */ + RemoveFDFromServerListenArray (client_fd, + rinit, + config_info->num_servers); + + /* + * exit readables for loop + */ + return; + } else if (bytes_read == 0) + { + /* + * make sure we don't try to read on this fd again + */ + FD_CLR(client_fd, rinit); + FD_CLR(client_fd, winit); + + (void) close (client_fd); + + if (client_conn_array[client_fd]->conn_to != -1) + { + /* + * mark this conn_fd fd ready to close + */ + int idx = client_conn_array[client_fd]->conn_to; + + client_conn_array[idx]->wclose = 1; + client_conn_array[idx]->conn_to = -1; + + /* + * but still force a last write on the conn_to connection + */ + FD_SET(client_conn_array[client_fd]->conn_to, winit); + } + + /* + * and mark this connection for no further activity + */ + client_conn_array[client_fd]->rbytes = 0; + client_conn_array[client_fd]->wbytes = 0; + client_conn_array[client_fd]->conn_to = -1; + + /* + * If this FD is for an Xserver must remove the server's + * listen fd so that clients will not attempt to connect + * on this fd. + */ + RemoveFDFromServerListenArray (client_fd, + rinit, + config_info->num_servers); + + } else + { + /* + * Move bytes between buffers on associated fd's + * (read data on one becomes write data on other) + */ + 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); + + /* + * check if you have more read data than available space + */ + if (client_conn_array[client_fd]->rbytes >= RWBUFFER_SIZE) + { + /* + * if so, abort the attempted copy until you can + * write some data out of the buffer first, and + * don't allow any more reading until that's done + */ + 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) + { + /* + * read the server reply to the authentication request + */ + (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; /* skip major & minor version, msg len */ + *(replyP+reasonLength+1) = '\0'; + (void) fprintf (stderr, "Server replied FAILURE: %s\n", replyP); + } + /* FALL-THROUGH */ +#endif + case SERVER_REPLY_SUCCESS: + /* + * two possibilities here: either the policy field + * passed to the server is unauthorized, or the server + * does not support the security extension; in both cases + * we read the client fd then synthesize a response + * which we forward to the client before closing the + * connection + */ + (void) read(client_conn_array[client_fd]->conn_to, + throw_away, 1); + /* + * construct the client response + */ + 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; + + /* + * calculate quadword padding required + */ + while (server_reason_remainder > 0) + server_reason_remainder = server_reason_remainder - four; + server_reason_remainder = abs(server_reason_remainder); + + /* + * allocate the padded buffer + */ + if ((server_reason_padded = + (char *) malloc (server_reason_len + + server_reason_remainder)) == NULL) + { + (void) fprintf (stderr, "malloc - server reason\n"); + return; + } + + /* + * calculate the "additional data" field + */ + prefix.length = (server_reason_len + server_reason_remainder) / + four; + + /* + * compare client and xfwp byte ordering and swap prefix fields + * as necessary + */ + endian = 1; + if (((throw_away[0] == '\154') && !(*(char *) &endian)) || + ((throw_away[0] == '\102') && (*(char *) &endian))) + { + /* + * client and xfwp are different byte order + * so swap all fwp 2-byte fields to little 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)); + } + + /* + * load the padded reason + */ + 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); + /* + * load the complete synthesized server reply (which will + * be sent to the client next time the writables are + * processed (again, to avoid blocking) + */ + 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; + + /* + * make sure to zero the wbytes on the client conn object + * before forwarding the server reply + */ + 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; + + /* + * clear the select() mask for the client socket and for + * the server + */ + FD_CLR(client_conn_array[client_fd]->conn_to, rinit); + FD_CLR(client_fd, rinit); +#ifdef DEBUG + /* + * output a trace message + */ + if (((int) client_conn_array[client_fd]->readbuf[0]) == + SERVER_REPLY_SUCCESS) + (void) fprintf (stderr, "Server replied SUCCESS\n"); +#endif + /* + * clean up memory + */ + 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: + /* + * the server supports the security extension; begin + * forwarding client and server messages normally + */ + 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; + + /* + * Check fd_counter to see if it is one of the PM listen fds + */ + for (i = 0; i < config_info->num_pm_listen_ports; i++) + { + if (pm_listen_array[i] == fd_counter) + { + if (!pm_conn_array[fd_counter]) + { + /* + * Process this new PM connection + */ + ProcessNewPMConnection (nfds, + rinit, + config_info, + listen_objects, + i); + return; + } + break; + } + } + + /* + * If this is an already-accepted PM connection, call + * IceProcessMessages() to invoke the FWPprocessMessages + * callback + */ + 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; + } + } + + /* + * Check fd_counter to see if it one of the ports that + * clients use to connect to a server. + */ + 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; + } + } + + /* + * At this point the input can be from the following sources: + * + * 1. An X server replying to an authentication request + * + * 2. Data coming from a X server that needs to be re-directed + * to a client + * + * 3. Data from a client that needs to be re-directed to + * its X server + */ + if (!client_conn_array[fd_counter]) + { + /* + * This can happen if the client was marked as closed by + * doProcessWritables. + */ + 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: + /* + * This is a do-nothing state while we're waiting for the + * server reply (see below) + */ + break; + + case SERVER_REPLY: + ProcessServerReply (rinit, + winit, + config_info, + fd_counter); + break; + + default: + /* + * All of the values of state have been tested. + */ + (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; + + /* + * Loop through descriptors + */ + for (fd_counter = 0; fd_counter < *nfds && *nready; fd_counter++) + { + /* + * Look at fd's for writables + */ + if (FD_ISSET(fd_counter, writable)) + { + /* + * Decrement the list of read/write ready connections + */ + *nready -= 1; + doProcessWritables (fd_counter, + rinit, + winit); + } + + /* + * Now, do the same thing for the readables + */ + if (FD_ISSET(fd_counter, readable)) + { + /* + * Decrement the list of read/write ready connections + */ + *nready -= 1; + doProcessReadables (fd_counter, + nfds, + rinit, + winit, + pm_listen_array, + config_info, + listen_objects); + } + } +} @@ -0,0 +1,53 @@ +/* $Xorg: io.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ +/* + +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. + +*/ + + +#ifndef _IO_H +#define _IO_H + +#define SERVER_REPLY_FAILURE 0 +#define SERVER_REPLY_SUCCESS 1 +#define SERVER_REPLY_AUTHENTICATE 2 + +extern 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); + +#endif /* _IO_H */ @@ -0,0 +1,1508 @@ +/* $Xorg: misc.c,v 1.6 2001/02/09 02:05:45 xorgcvs Exp $ */ + +/* +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <X11/Xos.h> +#include <netinet/in.h> +#include <arpa/inet.h> + +#include <X11/Xfuncs.h> /* Need for bcopy() */ +#include <X11/ICE/ICElib.h> +#include <X11/PM/PM.h> + +#include "xfwp.h" +#include "misc.h" +#include "pm.h" + +static Bool printConfigVerify = FALSE; +static Bool HaveSitePolicy = 0; + + +/*ARGSUSED*/ +static void +BadSyntax( + char *msg, + int line) +{ +#ifdef DEBUG + (void) fprintf(stderr, "Config error: %s at line %d\n", msg, line); +#endif +} + +static void +Usage() +{ + (void) fprintf (stderr, "Usage: xfwp [-pdt <#secs>] [-clt <#secs>] \\\n"); + (void) fprintf (stderr, + "\t[-cdt <#secs>] [-pmport <port#>] [-config <path>]\\\n"); + (void) fprintf (stderr, + "\t[-logfile <path>] [-loglevel <0|1>] [-verify]\n"); + exit (0); +} + +static void +MallocFailed() +{ + (void) fprintf(stderr, "Memory allocation failed, exiting\n"); + exit(1); +} + +static char* +Realloc( + char *p, + int s) +{ + if (!p) + p = malloc(s); + else + p = realloc(p, s); + + if (!p) + MallocFailed(); + + return p; +} + +static void +BadMalloc( + int line) +{ + (void) fprintf(stderr, "Error: memory exhaused at line %d\n", line); +} + +static void +doPrintEval( + struct config * config_info, + int line_counter) +{ + struct config_line *ruleP = config_info->config_file_data[line_counter]; + + if (!printConfigVerify) + return; + + (void) fprintf(stderr,"matched: %s %s %s %s %s %s %s\n", + (ruleP->permit_deny) ? ruleP->permit_deny : "", + (ruleP->source_hostname) ? ruleP->source_hostname : "", + (ruleP->source_netmask) ? ruleP->source_netmask : "", + (ruleP->dest_hostname) ? ruleP->dest_hostname : "", + (ruleP->dest_netmask) ? ruleP->dest_netmask : "", + (ruleP->operator) ? ruleP->operator : "", + (ruleP->service) ? ruleP->service : ""); +} + +static Bool +doConfigRequireDisallow( + int line, + char* result) +{ + Bool permit = (strcmp("require", result) == 0); + + if (((result = strtok(NULL, SEPARATOR1)) == NULL) || + (strcmp(result, "sitepolicy") != 0)) + { + BadSyntax("require/disallow must specify \"sitepolicy\"", line); + return 1; + } + + if (HaveSitePolicy && (SitePolicyPermit != permit)) + { + BadSyntax("can't mix require and disallow policies", line); + return 1; + } + + HaveSitePolicy = True; + SitePolicyPermit = permit; + + if ((result = strtok(NULL, " \n")) == NULL) + { + BadSyntax("missing policy string after \"sitepolicy\"", line); + return 1; + } + + if (SitePolicies) + SitePolicies = (char**)realloc((char**)SitePolicies, + (SitePolicyCount+1) * sizeof(char*)); + else + SitePolicies = (char**)malloc(sizeof(char*)); + + if (!SitePolicies) + { + BadMalloc(line); + return 1; + } + + SitePolicies[SitePolicyCount] = malloc(strlen(result) + 1); + + if (!SitePolicies[SitePolicyCount]) + { + BadMalloc(line); + return 1; + } + + strcpy(SitePolicies[SitePolicyCount++], result); + +#ifdef DEBUG + (void) fprintf(stderr, "%s %s", permit ? "requiring" : "disallowing", result); +#endif + + return False; +} + +static int +doVerifyHostMaskToken( + char token[]) +{ + char * result; + int delimiter_count = 0; + + /* + * verify there are 3 "." delimiters in the token + */ + while (token) + { + if ((result = strchr(token, SEPARATOR2)) != NULL) + { + token = result; + delimiter_count++; + token ++; + } else + token = result; + } + if ((delimiter_count < 3) || (delimiter_count > 3)) + return 0; + else + return 1; +} + +static int +doInitNewRule( + struct config *config_info) +{ + int rule_number = config_info->rule_count; + struct config_line *config_lineP; + + if (rule_number == config_info->lines_allocated) + { + if ((config_info->config_file_data = (struct config_line**) + Realloc((char*)config_info->config_file_data, + (config_info->lines_allocated += ADD_LINES) * + sizeof(struct config_line *))) == NULL) + { + (void) fprintf (stderr, "realloc - config_file_data\n"); + return -1; + } + } + + if ((config_lineP = (struct config_line *) + Malloc (sizeof(struct config_line))) == NULL) + { + (void) fprintf (stderr, "malloc - config_lineP\n"); + return -1; + } + + config_lineP->permit_deny = NULL; + config_lineP->source_hostname = NULL; + config_lineP->source_host = 0; + config_lineP->source_netmask = NULL; + config_lineP->source_net = 0; + config_lineP->dest_hostname = NULL; + config_lineP->dest_host = 0; + config_lineP->dest_netmask = NULL; + config_lineP->dest_net = 0; + config_lineP->operator = NULL; + config_lineP->service = NULL; + + config_info->config_file_data[rule_number] = config_lineP; + + return rule_number; +} + +static int +doConfigPermitDeny( + struct config *config_info, + char *result) +{ + struct config_line ** config_file_data; + int line_number; + int bad_token = 0; + + /* + * caution; config_info->config_file_data can move in doInitNewRule + */ + if ((line_number = doInitNewRule(config_info)) == -1) + return 1; + + config_file_data = config_info->config_file_data; + + if ((config_file_data[line_number]->permit_deny = + (char *) malloc (strlen(result) + 1)) == NULL) + { + (void) fprintf(stderr, "malloc - config rule (permit/deny keyword)\n"); + return 0; + } + strcpy(config_file_data[line_number]->permit_deny, result); + +#ifdef DEBUG + (void) fprintf(stderr, + "first token = %s\n", + config_file_data[line_number]->permit_deny); +#endif + + /* + * do the source hostname field + */ + if ((result = strtok(NULL, SEPARATOR1)) != NULL) + { + char token[64]; + strcpy(token, result); + + if (doVerifyHostMaskToken(token)) + { + if ((config_file_data[line_number]->source_hostname = + (char *) malloc (strlen(result) + 1)) == NULL) + { + (void) fprintf(stderr, "malloc - config rule (source host)\n"); + return 0; + } + strcpy(config_file_data[line_number]->source_hostname, result); +#ifdef DEBUG + (void) fprintf(stderr, + "second token = %s\n", + config_file_data[line_number]->source_hostname); +#endif + /* + * generate network address format + */ + config_file_data[line_number]->source_host = + inet_addr(config_file_data[line_number]->source_hostname); + } else + bad_token = 1; + } + + /* + * now the source netmask field + */ + if ((result = strtok(NULL, SEPARATOR1)) != NULL) + { + char token[64]; + strcpy(token, result); + + if (doVerifyHostMaskToken(token)) + { + if ((config_file_data[line_number]->source_netmask = + (char *) malloc (strlen(result) + 1)) == NULL) + { + (void) fprintf(stderr, "malloc - config rule (source netmask)\n"); + return 0; + } + strcpy(config_file_data[line_number]->source_netmask, result); +#ifdef DEBUG + (void) fprintf(stderr, + "third token = %s\n", + config_file_data[line_number]->source_netmask); +#endif + config_file_data[line_number]->source_net = + inet_addr(config_file_data[line_number]->source_netmask); + } else + bad_token = 1; + } + + /* + * now the destination hostname field + */ + if ((result = strtok(NULL, SEPARATOR1)) != NULL) + { + char token[64]; + strcpy(token, result); + + if (doVerifyHostMaskToken(token)) + { + if ((config_file_data[line_number]->dest_hostname = + (char *) malloc (strlen(result) + 1)) == NULL) + { + (void) fprintf(stderr, "malloc - config rule (destination host)\n"); + return 0; + } + strcpy(config_file_data[line_number]->dest_hostname, result); +#ifdef DEBUG + (void) fprintf(stderr, + "fourth token = %s\n", + config_file_data[line_number]->dest_hostname); +#endif + config_file_data[line_number]->dest_host = + inet_addr(config_file_data[line_number]->dest_hostname); + } else + bad_token = 1; + } + + /* + * now the destination netmask field + */ + if ((result = strtok(NULL, SEPARATOR1)) != NULL) + { + char token[64]; + strcpy(token, result); + + if (doVerifyHostMaskToken(token)) + { + if ((config_file_data[line_number]->dest_netmask = + (char *) malloc (strlen(result) + 1)) == NULL) + { + (void) fprintf(stderr, "malloc - config rule (destination mask)\n"); + return 0; + } + strcpy(config_file_data[line_number]->dest_netmask, result); +#ifdef DEBUG + (void) fprintf(stderr, + "fifth token = %s\n", + config_file_data[line_number]->dest_netmask); +#endif + config_file_data[line_number]->dest_net = + inet_addr(config_file_data[line_number]->dest_netmask); + } else + bad_token = 1; + } + + /* + * now the operator field + */ + if ((result = strtok(NULL, SEPARATOR1)) != NULL) + { + if (!strcmp("eq", result)) + { + if ((config_file_data[line_number]->operator = + (char *) malloc (strlen(result) + 1)) == NULL) + { + (void) fprintf(stderr, "malloc - config rule (op)\n"); + return 0; + } + strcpy(config_file_data[line_number]->operator, result); +#ifdef DEBUG + (void) fprintf(stderr, + "sixth token = %s\n", + config_file_data[line_number]->operator); +#endif + } else + bad_token = 1; + } + + /* + * and finally the service field + */ + if ((result = strtok(NULL, SEPARATOR1)) != NULL) + { + if (!(strncmp("pm", result, 2)) || + (!strncmp("fp", result, 2)) || + (!strncmp("cd", result, 2))) + { + if ((config_file_data[line_number]->service = + (char *) malloc (strlen(result) + 1)) == NULL) + { + (void) fprintf(stderr, "malloc - config rule (service)\n"); + return 0; + } + strcpy(config_file_data[line_number]->service, result); +#ifdef DEBUG + (void) fprintf(stderr, + "seventh token = %s\n", + config_file_data[line_number]->service); +#endif + /* + * load the appropriate service id + */ + if (!strncmp(config_file_data[line_number]->service, "pm", 2)) + config_file_data[line_number]->service_id = PMGR; + else if (!strncmp(config_file_data[line_number]->service, "fp", 2)) + config_file_data[line_number]->service_id = FINDPROXY; + else + if (!strncmp(config_file_data[line_number]->service, "cd", 2)) + config_file_data[line_number]->service_id = CLIENT; + } else + bad_token = 1; + } + + /* + * rules for bad parse + */ + if (bad_token || + (config_file_data[line_number]->permit_deny == NULL) || + ((config_file_data[line_number]->permit_deny != NULL) && + (config_file_data[line_number]->source_hostname == NULL)) || + ((config_file_data[line_number]->source_hostname != NULL) && + (config_file_data[line_number]->source_netmask == NULL)) || + ((config_file_data[line_number]->dest_hostname != NULL) && + (config_file_data[line_number]->dest_netmask == NULL)) || + ((config_file_data[line_number]->operator != NULL) && + (config_file_data[line_number]->service == NULL))) + return 1; + + config_info->rule_count++; + return 0; +} + +static int +doProcessLine( + char *line, + struct config *config_info, + int config_line) +{ + char * result; + int bad_parse = 0; + + if (line[0] == '#' || line[0] == '\n') + return 1; + + if ((result = strtok(line, SEPARATOR1)) != NULL) + { + if (!(strcmp("permit", result)) || (!strcmp("deny", result))) + { + bad_parse = doConfigPermitDeny(config_info, result); + } + else + if (!strcmp("require", result) || !strcmp("disallow", result)) + bad_parse = doConfigRequireDisallow(config_line, result); + + else + bad_parse = 1; + } + + if (bad_parse) + return 0; + else + return 1; +} + +/* + * Public functions + */ +char* +Malloc( + int s) +{ + char *p = malloc(s); + + if (!p) + MallocFailed(); + + return p; +} + +int +doConfigCheck( + struct sockaddr_in * source_sockaddr_in, + struct sockaddr_in * dest_sockaddr_in, + struct config * config_info, + int context, + int * rule_number) +{ + int line_counter; + + /* + * look through the config file parse tree for a source IP address + * that matches this request + */ + for (line_counter = 0; line_counter < config_info->rule_count; line_counter++) + { + if (config_info->config_file_data[line_counter] != NULL) + { + if ((source_sockaddr_in->sin_addr.s_addr & + (~(config_info->config_file_data[line_counter]->source_net))) == + config_info->config_file_data[line_counter]->source_host) + { + /* + * okay, the source host and netmask fields pass, see if the + * config file specifies "permit" or "deny" for this host + */ + if (!strcmp(config_info->config_file_data[line_counter]->permit_deny, + "permit")) + { + /* + * check for presence of destination info + */ + if ((config_info->config_file_data[line_counter]->dest_hostname) && + (context != PMGR)) + { + /* + * compute destination info restrictions + */ + if ((dest_sockaddr_in->sin_addr.s_addr & + (~(config_info->config_file_data[line_counter]->dest_net))) == + config_info->config_file_data[line_counter]->dest_host) + { + /* + * you got a match on the destination, so look at + * the operator and service fields to see if the "permit" + * might be specific to one particular connection-type only + */ + if (config_info->config_file_data[line_counter]->operator != NULL) + { + /* + * there *is* a service id; see if it matches our current + * config check request + */ + if (config_info->config_file_data[line_counter]->service_id == + context) + { + doPrintEval(config_info, line_counter); + /* + * if you are permitting, there's no rule match to log + */ + *rule_number = line_counter + 1; + return 1; + } else + /* + * we didn't get a match on context; this "permit" doesn't + * apply to the current request; so keep trying + */ + continue; + } else + /* + * there's no service qualifier; permit the connection + */ + doPrintEval(config_info, line_counter); + *rule_number = line_counter + 1; + return 1; + } else + /* + * the destination field doesn't match; keep trying + */ + continue; + } else if ((config_info-> + config_file_data[line_counter]->dest_hostname) && + (context == PMGR)) + { + /* + * skip the destination address check and test for + * the operator and service_id + */ + if (config_info->config_file_data[line_counter]->operator != NULL) + { + /* + * there *is* a service id; see if it matches our current + * config check context + */ + if (config_info->config_file_data[line_counter]->service_id + == context) + { + doPrintEval(config_info, line_counter); + /* + * not logging PM events so don't save rule match + */ + return 1; + } else + /* + * we didn't get a match on context; this "permit" doesn't + * apply to the current client request; so keep trying + */ + continue; + } else + { + /* + * there's no service qualifier; permit the connection + */ + doPrintEval(config_info, line_counter); + *rule_number = line_counter + 1; + return 1; + } + } else + { + /* + * there's no destination specified; permit the connection + */ + doPrintEval(config_info, line_counter); + *rule_number = line_counter + 1; + return 1; + } + } + else + { + /* + * we still have to check the destination and service fields + * to know exactly what we are denying + */ + if ((config_info->config_file_data[line_counter]->dest_hostname) && + (context != PMGR)) + { + /* + * compute destination info restrictions + */ + if ((dest_sockaddr_in->sin_addr.s_addr & + (~(config_info->config_file_data[line_counter]->dest_net))) == + config_info->config_file_data[line_counter]->dest_host) + { + /* + * you got a match on the destination, so look at + * the operator and service fields to see if the "deny" + * might be specific to one particular connection-type only + */ + if (config_info->config_file_data[line_counter]->operator != NULL) + { + /* + * there *is* a service id; see if it matches our current + * config check request + */ + if (config_info->config_file_data[line_counter]->service_id == + context) + { + /* + * the match signifies an explicit denial of permission + */ + doPrintEval(config_info, line_counter); + /* + * save the rule match number before returning + */ + *rule_number = line_counter + 1; + return 0; + } else + /* + * we didn't get a match on the service id; the "deny" + * operation doesn't apply to this connection, so keep + * trying + */ + continue; + } else + { + /* + * there's no service qualifier; deny the connection + */ + doPrintEval(config_info, line_counter); + /* + * save the rule match number before returning + */ + *rule_number = line_counter + 1; + return 0; + } + } else + /* + * the destination field doesn't match; keep trying + */ + continue; + } else if ((config_info-> + config_file_data[line_counter]->dest_hostname) && + (context == PMGR)) + { + /* + * skip the destination address check and test for + * the operator and service_id + */ + if (config_info->config_file_data[line_counter]->operator != NULL) + { + /* + * there *is* a service id; see if it matches our current + * config check context + */ + if (config_info->config_file_data[line_counter]->service_id == + context) + { + /* + * this is a request to explicitly deny service, so do it + */ + doPrintEval(config_info, line_counter); + /* + * not logging PM events, but if we were, save rule match here + */ + return 0; + } else + /* + * we didn't get a match on context; this "deny" doesn't + * apply to the current client request; so keep trying + */ + continue; + } else + { + /* + * there's no service qualifier; deny the connection + */ + doPrintEval(config_info, line_counter); + /* + * if we were logging PM events ... + */ + return 0; + } + } else + { + /* + * there's no destination specified; deny the connection + */ + doPrintEval(config_info, line_counter); + /* + * save rule match + */ + *rule_number = line_counter + 1; + return 0; + } + } /* end else deny */ + } /* end if match on source */ + } /* end if valid config line */ + } /* end all config lines for loop */ + + /* + * whatever you did not explicitly permit you must deny -- *unless* -- + * no config file was specified, in which case permit all + */ + if (config_info->config_file_path == NULL) + { + if (printConfigVerify) + (void) fprintf(stderr, + "matched default permit 0.0.0.0 255.255.255.255\n"); + /* + * there's no rule match to save + */ + *rule_number = -1; + return 1; + } + + if (!config_info->config_file_data) + { + /* + * A config file was specified but it had no permit/deny + * entries. This can happen if: the file was empty; the + * file only contained comment entries; the file only has + * require/disallow entries. In any event, this case + * should be treated as if no config file was used - that + * is allow the connection. + */ + *rule_number = -1; + return 1; + } + + if (printConfigVerify) + (void) fprintf(stderr, "matched default deny 0.0.0.0 255.255.255.255\n"); + + /* + * not in this case either + */ + *rule_number = -1; + return 0; +} + + +void +doCheckTimeouts( + struct config * config_info, + int * nfds_ready, + fd_set * rinit, + fd_set * winit, + fd_set * readable, + fd_set * writable) +{ + int client_data_counter; + int client_listen_counter; + int pm_conn_counter; + struct timeval current_time; + struct timezone current_zone; + + /* + * get current time + */ + gettimeofday(¤t_time, ¤t_zone); + + /* + * start with the clients; we have to do them all, because a + * timeout may occur even if the object's fd is not currently + * readable or writable + */ + for (client_data_counter = 0; + client_data_counter < config_info->num_client_conns; + client_data_counter++) + { + if (client_conn_array[client_data_counter] != NULL) + { + /* + * do the shutdown time computation + */ + if ((current_time.tv_sec + - client_conn_array[client_data_counter]->creation_time) + > client_conn_array[client_data_counter]->time_to_close) + { + /* + * time to shut this client conn down; we're not going to be graceful + * about it, either; we're just going to clear the select() masks for + * the relevant file descriptors, close these fd's and deallocate + * the connection objects (for both client and server), and finally + * adjust the select() return params as necessary + */ + FD_CLR(client_conn_array[client_data_counter]->fd, rinit); + FD_CLR(client_conn_array[client_data_counter]->fd, winit); + FD_CLR(client_conn_array[client_data_counter]->conn_to, rinit); + FD_CLR(client_conn_array[client_data_counter]->conn_to, winit); + close(client_conn_array[client_data_counter]->fd); + close(client_conn_array[client_data_counter]->conn_to); + free(client_conn_array[client_conn_array[client_data_counter]->conn_to]); + if (client_conn_array[client_data_counter]->source) + free(client_conn_array[client_data_counter]->source); + if (client_conn_array[client_data_counter]->destination) + free(client_conn_array[client_data_counter]->destination); + free(client_conn_array[client_data_counter]); + client_conn_array[client_conn_array[client_data_counter]->conn_to] = + NULL; + client_conn_array[client_data_counter] = NULL; + /* + * the nfds_ready value is tricky, because we're not sure if we got + * a readable or writable on the associated connection for this + * iteration through select(); we'll decrement it one instead of two, + * but it really doesn't matter either way given the logic of the + * process readables and writables code + */ + *nfds_ready--; + /* + * if you just shut this connection object down, you don't want + * to reset its creation date to now, so go to the next one + */ + continue; + } + /* + * recompute select() timeout to maximize blocking time without + * preventing timeout checking + */ + config_info->select_timeout.tv_sec = + min(config_info->select_timeout.tv_sec, + client_conn_array[client_data_counter]->time_to_close - + (current_time.tv_sec - + client_conn_array[client_data_counter]->creation_time)); + /* + * this wasn't a shutdown case, so check to see if there's activity + * on the fd; if so, then reset the creation time field to now + */ + if (FD_ISSET(client_conn_array[client_data_counter]->fd, readable) || + FD_ISSET(client_conn_array[client_data_counter]->fd, writable)) + client_conn_array[client_data_counter]->creation_time = + current_time.tv_sec; + /* + * do the same thing with the conn_to connections, but only + * if they haven't already been marked for closing + */ + if ((client_conn_array[client_data_counter]->conn_to) > 0) + { + + if ((FD_ISSET(client_conn_array[client_data_counter]->conn_to, + readable)) || + (FD_ISSET(client_conn_array[client_data_counter]->conn_to, + writable))) + client_conn_array[client_data_counter]->creation_time = + current_time.tv_sec; + } + } + } + + /* + * now do the client listen fds; as with the client data objects, + * we have to do them all, because a timeout may occur even if the + * object's fd is not currently readable or writable + */ + for (client_listen_counter = 0; + client_listen_counter < config_info->num_servers; + client_listen_counter++) + { + if (server_array[client_listen_counter] != NULL) + { + /* + * do the shutdown time computation + */ + if ((current_time.tv_sec + - server_array[client_listen_counter]->creation_time) + > server_array[client_listen_counter]->time_to_close) + { + /* + * time to shut this listener down just like we did above; + * we close the server connection here as well but we don't + * need to worry about the select() mask because we're not + * using the server fd in this object for reading or writing -- + * only to initialize the client data object server connections + */ + FD_CLR(server_array[client_listen_counter]->client_listen_fd, rinit); + FD_CLR(server_array[client_listen_counter]->client_listen_fd, winit); + close(server_array[client_listen_counter]->client_listen_fd); + free(server_array[client_listen_counter]); + server_array[client_listen_counter] = NULL; + *nfds_ready--; + /* + * if you just shut this connection object down, you don't want + * to reset its creation date to now, so go to the next one + */ + continue; + } + /* + * recompute select() timeout to maximize blocking time without + * preventing timeout checking + */ + config_info->select_timeout.tv_sec = + min(config_info->select_timeout.tv_sec, + server_array[client_listen_counter]->time_to_close - + (current_time.tv_sec - + server_array[client_listen_counter]->creation_time)); + /* + * this wasn't a shutdown case, so check to see if there's activity + * on the fd; if so, then reset the creation time field to now + */ + if (FD_ISSET(server_array[client_listen_counter]->client_listen_fd, + readable) || + FD_ISSET(server_array[client_listen_counter]->client_listen_fd, + writable)) + server_array[client_listen_counter]->creation_time = + current_time.tv_sec; + } + } + + /* + * last of all the pm connection fds + */ + for (pm_conn_counter = 0; + pm_conn_counter < config_info->num_pm_conns; + pm_conn_counter++) + { + if (pm_conn_array[pm_conn_counter] != NULL) + { + /* + * do the shutdown time computation + */ + if ((current_time.tv_sec + - pm_conn_array[pm_conn_counter]->creation_time) + > pm_conn_array[pm_conn_counter]->time_to_close) + { + /* + * shut this connection down just like the others + */ + FD_CLR(pm_conn_array[pm_conn_counter]->fd, rinit); + FD_CLR(pm_conn_array[pm_conn_counter]->fd, winit); + close(pm_conn_array[pm_conn_counter]->fd); + free(pm_conn_array[pm_conn_counter]); + pm_conn_array[pm_conn_counter] = NULL; + *nfds_ready--; + /* + * if you just shut this connection object down, you don't want + * to reset its creation date to now, so go to the next one + */ + continue; + } + /* + * recompute select() timeout to maximize blocking time without + * preventing timeout checking + */ + config_info->select_timeout.tv_sec = + min(config_info->select_timeout.tv_sec, + pm_conn_array[pm_conn_counter]->time_to_close - + (current_time.tv_sec - + pm_conn_array[pm_conn_counter]->creation_time)); + /* + * this wasn't a shutdown case, so check to see if there's activity + * on the fd; if so, then reset the creation time field to now + */ + if (FD_ISSET(pm_conn_array[pm_conn_counter]->fd, readable) || + FD_ISSET(pm_conn_array[pm_conn_counter]->fd, writable)) + pm_conn_array[pm_conn_counter]->creation_time = current_time.tv_sec; + } + } +} + + +int +doHandleConfigFile ( + struct config * config_info) +{ + FILE * stream; + char line[128]; + int num_chars = 120; + int line_number = 0; + + if (!config_info->config_file_path) + return 1; + + if ((stream = fopen(config_info->config_file_path, "r")) == NULL) + { + perror("Could not open config file"); + return 0; + } + + while (1) + { + if ((fgets(line, num_chars, stream)) == NULL) + { +#ifdef DEBUG + (void) fprintf(stderr, "Reading config file - got 0 bytes\n"); +#endif + break; + } + +#ifdef DEBUG + (void) fprintf(stderr, line); +#endif + + line_number++; + + if (!doProcessLine(line, config_info, line_number)) + { + (void) fprintf(stderr,"Config file format error. Parse failed.\n"); + (void) fprintf(stderr,"\tline: %s\n", line); + (void) fclose(stream); + return 0; + } + } + + if (!feof(stream)) + { + (void) fprintf(stderr, "Error parsing config file; not at eof\n"); + (void) fclose(stream); + return 0; + } + + if (printConfigVerify) + (void) fprintf(stderr, "%d rules read\n", config_info->rule_count); + + (void) fclose(stream); + return 1; +} + +void +doWriteLogEntry( + char * source, + char * destination, + int event, + int rule_number, + struct config * config_info) +{ + FILE * stream; + struct timezone current_zone; + struct timeval current_time; + char * time_stamp; + int time_length; + + /* + * if no logfile, then return without action + */ + if (!config_info->log_file_path) + return; + + /* + * The xfwp audit/logging spec says to always log CLIENT_REJECT_CONFIG + * events but other events should only be logged if log_level is + * > 0 + */ + if (event != CLIENT_REJECT_CONFIG && !config_info->log_level) + return; + + if ((stream = fopen(config_info->log_file_path, "a")) == NULL) + { + (void) fprintf(stderr, + "Failed to open log file '%s'\n", + config_info->log_file_path); + return; + } + + /* + * generate time stamp for this event + */ + gettimeofday(¤t_time, ¤t_zone); + time_stamp = ctime((time_t *) ¤t_time.tv_sec); + time_length = strlen(time_stamp); + + /* + * eliminate newline character in time stamp + */ + *(&time_stamp[time_length - 1]) = (char) 0; + + (void) fprintf (stream, "%s %2d %s %s %2d\n", + time_stamp, + event, + (source) ? source : "", + (destination) ? destination : "", + rule_number); + + (void) fclose(stream); +} + + +void +doCopyFromTo( + int fd_from, + int fd_to, + fd_set * rinit, + fd_set * winit) +{ + int ncopy; + + if (client_conn_array[fd_from]->wbytes < RWBUFFER_SIZE) + { + /* + * choose to write either how much you have (from->rbytes), + * or how much you can hold (to->wbytes), whichever is + * smaller + */ + ncopy = min(client_conn_array[fd_from]->rbytes, + RWBUFFER_SIZE - client_conn_array[fd_to]->wbytes); + /* + * index into existing number bytes into the write buffer + * to get the start point for copying + */ + bcopy(client_conn_array[fd_from]->readbuf, + client_conn_array[fd_to]->writebuf + + client_conn_array[fd_to]->wbytes, ncopy); + /* + * Then up the to->wbytes counter + */ + client_conn_array[fd_to]->wbytes += ncopy; + /* + * something has to be done here with the select mask!! + */ + FD_SET(fd_to, winit); + if (ncopy == client_conn_array[fd_from]->rbytes) + client_conn_array[fd_from]->rbytes = 0; + else + { + bcopy(client_conn_array[fd_from]->readbuf + ncopy, + client_conn_array[fd_from]->readbuf, + client_conn_array[fd_from]->rbytes - ncopy); + client_conn_array[fd_from]->rbytes -= ncopy; + } + /* + * and here + */ + FD_SET(fd_to, rinit); + } + /* + * If there's no room in the fd_to write buffer, do nothing + * this iteration (keep iterating on select() until something + * gets written from this fd) + */ + return; +} + + +int +doCheckServerList( + char * server_address, + char ** listen_port_string, + int num_servers) +{ + /* + * this routine checks the server_address (provided by XFindProxy + * and forwarded through the PM to the FWP) against the list of + * servers to which connections have already been established; + * it does no format type checking or conversions! (i.e., network-id + * vs. hostname representations); if the string received is not an + * exact match to one in the list, FWP will open a new connection + * to the specified server, even though one may already exist under + * a different name-format; all this is in a separate routine in + * case we want to check the various formats in the future + */ + int list_counter; + + for (list_counter = 0; list_counter < num_servers; list_counter++) + { + if (server_array[list_counter] != NULL) + { + if (!strcmp(server_array[list_counter]->x_server_hostport, + server_address)) + { + /* + * allocate and return the listen_port_string + */ + if ((*listen_port_string = (char *) malloc + (strlen(server_array[list_counter]->listen_port_string) + 1)) + == NULL) + { + (void) fprintf(stderr, "malloc - listen_port_string\n"); + return FAILURE; + } + strcpy(*listen_port_string, + server_array[list_counter]->listen_port_string); + return SUCCESS; + } + } + } + return FAILURE; +} + + +void +doProcessInputArgs ( + struct config * config_info, + int argc, + char * argv[]) +{ + int arg_counter; + int break_flag = 0; + + config_info->num_servers = 0; + config_info->num_pm_conns = 0; + config_info->pm_data_timeout = 0; + config_info->client_listen_timeout = 0; + config_info->client_data_timeout = 0; + config_info->log_level = 0; + config_info->rule_count = config_info->lines_allocated = 0; + config_info->pm_listen_port = NULL; + config_info->config_file_data = NULL; + config_info->config_file_path = NULL; + config_info->log_file_path = NULL; + + /* + * initialize timeout for three port types; if a timeout for a + * particular port type (pmdata, clientlisten, clientdata) is + * not specified explicitly, then it assumes the hard-coded + * default value; initialize other command line options here + * as well + */ + for (arg_counter = 1; arg_counter < argc; arg_counter++) + { + if (argv[arg_counter][0] == '-') + { + if (!strcmp("-pdt", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + config_info->pm_data_timeout = atoi(argv[arg_counter + 1]); + } + else if (!strcmp("-clt", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + config_info->client_listen_timeout = atoi(argv[arg_counter + 1]); + } + else if (!strcmp("-cdt", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + config_info->client_data_timeout = atoi(argv[arg_counter + 1]); + } + else if (!strcmp("-pmport", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + if (atoi(argv[arg_counter + 1]) > 65536) + { + break_flag = 1; + break; + } + config_info->pm_listen_port = Malloc(strlen(argv[arg_counter+1])+1); + strcpy(config_info->pm_listen_port, argv[arg_counter + 1]); + } + else if (!strcmp("-max_pm_conns", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + config_info->num_pm_conns = atoi(argv[arg_counter + 1]); + } + else if (!strcmp("-max_server_conns", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + config_info->num_servers = atoi(argv[arg_counter + 1]); + } + else if (!strcmp("-config", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + config_info->config_file_path = Malloc(strlen(argv[arg_counter+1])+1); + strcpy(config_info->config_file_path, argv[arg_counter + 1]); + } + else if (!strcmp("-verify", argv[arg_counter])) + { + printConfigVerify = TRUE; + } + else if (!strcmp("-logfile", argv[arg_counter])) + { + if (arg_counter + 1 == argc) + { + break_flag = 1; + break; + } + config_info->log_file_path = Malloc(strlen(argv[arg_counter+1])+1); + strcpy(config_info->log_file_path, argv[arg_counter + 1]); + } + else if (!strcmp("-loglevel", argv[arg_counter])) + { + if ((arg_counter + 1 == argc) || (atoi(argv[arg_counter + 1]) > 1)) + { + break_flag = 1; + break; + } + config_info->log_level = atoi(argv[arg_counter + 1]); + } + else + { + (void) fprintf(stderr, "Unrecognized command line argument\n"); + Usage(); + } + } + } + if (break_flag) + Usage(); + + /* + * Create space for the global connection arrays + */ +#ifdef XNO_SYSCONF /* should only be on FreeBSD 1.x and NetBSD 0.x */ +#undef _SC_OPEN_MAX +#endif +#ifdef _SC_OPEN_MAX + config_info->num_client_conns = sysconf(_SC_OPEN_MAX) - 1; +#else +#ifdef hpux + config_info->num_client_conns = _NFILE - 1; +#else + config_info->num_client_conns = getdtablesize() - 1; +#endif +#endif + + client_conn_array = (struct client_conn_buf **) + malloc (config_info->num_client_conns * sizeof (struct client_conn_buf *)); + if (!client_conn_array) + { + (void) fprintf (stderr, "malloc - client connection array\n"); + exit (1); + } + + if (!config_info->num_pm_conns) + config_info->num_pm_conns = MAX_PM_CONNS; + pm_conn_array = (struct pm_conn_buf **) + malloc (config_info->num_client_conns * sizeof (struct pm_conn_buf *)); + if (!pm_conn_array) + { + (void) fprintf (stderr, "malloc - PM connection array\n"); + exit (1); + } + + if (!config_info->num_servers) + config_info->num_servers = MAX_SERVERS; + server_array = (struct server_list **) + malloc (config_info->num_servers * sizeof (struct server_list *)); + if (!server_array) + { + (void) fprintf (stderr, "malloc - server listen array\n"); + exit (1); + } + + /* + * check timeout values; if still zero then apply defaults + */ + if (config_info->pm_data_timeout <= 0) + config_info->pm_data_timeout = PM_DATA_TIMEOUT_DEFAULT; + if (config_info->client_listen_timeout <= 0) + config_info->client_listen_timeout = CLIENT_LISTEN_TIMEOUT_DEFAULT; + if (config_info->client_data_timeout <= 0) + config_info->client_data_timeout = CLIENT_DATA_TIMEOUT_DEFAULT; + if (config_info->pm_listen_port == NULL) + { + config_info->pm_listen_port = Malloc(strlen(PM_LISTEN_PORT) + 1); + strcpy(config_info->pm_listen_port, PM_LISTEN_PORT); + } +} + + +int +doInitDataStructs( + struct config * config_info, + struct ICE_setup_info * pm_conn_setup) +{ + int i; + + /* + * Initialize select() timeout; start with a high value, which will + * be overridden by the minimum timeout value of all fd's taken + * together;this heuristic allows us to block inside select() + * as much as possible (avoiding CPU spin cycles), as well as + * to periodically check timeouts on open ports and thereby recover + * them + */ + config_info->select_timeout.tv_usec = 0; + config_info->select_timeout.tv_sec = 180000; + + /* + * NULL the connection arrays + */ + for (i = 0; i < config_info->num_client_conns; i++) + client_conn_array[i] = NULL; + for (i = 0; i < config_info->num_pm_conns; i++) + pm_conn_array[i] = NULL; + for (i = 0; i < config_info->num_servers; i++) + server_array[i] = NULL; + + /* + * init ICE connection setup data + */ + pm_conn_setup->opcode = 0; + pm_conn_setup->versionCount = 1; + pm_conn_setup->PMVersions->major_version = 1; + pm_conn_setup->PMVersions->minor_version = 0; + pm_conn_setup->PMVersions->process_msg_proc = + (IcePaProcessMsgProc) FWPprocessMessages; + /* + * Register for protocol setup + */ + if ((pm_conn_setup->opcode = IceRegisterForProtocolReply( + PM_PROTOCOL_NAME, + "XC", + "1.0", + pm_conn_setup->versionCount, + pm_conn_setup->PMVersions, + 0, /* authcount */ + NULL, /* authname */ + NULL, /* authprocs */ + FWPHostBasedAuthProc, /* force non-auth */ + FWPprotocolSetupProc, + NULL, /* protocol activate proc */ + NULL /* IceIOErrorProc */ )) < 0) + { + /* + * Log this message? + */ + (void) fprintf(stderr, "Could not register PROXY_MANAGEMENT ICE protocol"); + return FAILURE; + } + + global_data.major_opcode = pm_conn_setup->opcode; + + return SUCCESS; +} + @@ -0,0 +1,111 @@ +/* $Xorg: misc.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ +/* + +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. + +*/ + +#ifndef _MISC_H +#define _MISC_H + +/* + * the following are timeout defaults (in seconds) on the three + * types of ports (pm-data, client-listen and client-data) + */ +#define PM_DATA_TIMEOUT_DEFAULT 3600 /* 1 hour */ +#define CLIENT_LISTEN_TIMEOUT_DEFAULT 86400 /* 24 hours */ +#define CLIENT_DATA_TIMEOUT_DEFAULT 604800 /* 1 week */ + +#define PM_LISTEN_PORT "4444" + +/* + * allocate ADD_LINES entries at a time when loading the configuration + */ +#define ADD_LINES 20 +#define SEPARATOR1 " \t\n" +#define SEPARATOR2 '.' + + +extern char* +Malloc( + int s); + +extern int +doConfigCheck( + struct sockaddr_in * source_sockaddr_in, + struct sockaddr_in * dest_sockaddr_in, + struct config * config_info, + int context, + int * rule_number); + +extern void +doCheckTimeouts( + struct config * config_info, + int * nfds_ready, + fd_set * rinit, + fd_set * winit, + fd_set * readable, + fd_set * writable); + +extern int +doHandleConfigFile ( + struct config * config_info); + +extern void +doWriteLogEntry( + char * source, + char * destination, + int event, + int rule_number, + struct config * config_info); + +extern void +doCopyFromTo( + int fd_from, + int fd_to, + fd_set * rinit, + fd_set * winit); + +extern int +doCheckServerList( + char * server_address, + char ** listen_port_string, + int num_servers); + +extern void +doProcessInputArgs( + struct config * config_info, + int argc, + char * argv[]); + +extern int +doInitDataStructs( + struct config * config_info, + struct ICE_setup_info * PM_conn_setup); + +#endif /* _MISC_H */ @@ -0,0 +1,538 @@ +/* $Xorg: pm.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ +/* + +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <X11/Xos.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <signal.h> +#include <X11/Xproto.h> + +#include <X11/ICE/ICElib.h> +#include <X11/ICE/ICEmsg.h> +#include <X11/ICE/ICEproto.h> + +#include <X11/PM/PM.h> +#include <X11/PM/PMproto.h> + +#include "xfwp.h" +#include "pm.h" +#include "transport.h" +#include "misc.h" + +void FWPprocessMessages( + IceConn iceConn, + IcePointer * client_data, + int opcode, + unsigned long length, + Bool swap) +{ + switch (opcode) + { + /* + * this is really the only opcode we care about -- the one + * which indicates an XFindProxy request for a connection + * to a specified server + */ + case PM_GetProxyAddr: + { + pmGetProxyAddrMsg *pMsg; + char *pData, *pStart; + char *serviceName = NULL, *serverAddress = NULL; + char *hostAddress = NULL, *startOptions = NULL; + char *authName = NULL, *authData = NULL; + int authLen; + struct clientDataStruct * program_data; + char * listen_port_string; + int pm_send_msg_len; + pmGetProxyAddrReplyMsg * pReply; + char * pReplyData; + struct hostent * hostptr; + struct sockaddr_in server_sockaddr_in; + struct sockaddr_in dummy_sockaddr_in; + char * server_name_base; + char * config_failure = "unrecognized server or permission denied"; + char * tmp_str; + int rule_number = -1; + char * colon; + char * tmpAddress = NULL; + + /* + * this is where we need and get access to that client data we + * went through such contortions to set up earlier! + */ + program_data = (struct clientDataStruct *) client_data; + /* + * initial check on expected message size + */ + CHECK_AT_LEAST_SIZE (iceConn, global_data.major_opcode, opcode, + length, SIZEOF (pmGetProxyAddrMsg), IceFatalToProtocol); + + IceReadCompleteMessage (iceConn, SIZEOF (pmGetProxyAddrMsg), + pmGetProxyAddrMsg, pMsg, pStart); + + if (!IceValidIO (iceConn)) + { + IceDisposeCompleteMessage (iceConn, pStart); + return; + } + + authLen = swap ? lswaps (pMsg->authLen) : pMsg->authLen; + + pData = pStart; + + SKIP_STRING (pData, swap); /* proxy-service */ + SKIP_STRING (pData, swap); /* server-address */ + SKIP_STRING (pData, swap); /* host-address */ + SKIP_STRING (pData, swap); /* start-options */ + if (authLen > 0) + { + SKIP_STRING (pData, swap); /* auth-name */ + pData += (authLen + PAD64 (authLen)); /* auth-data */ + } + /* + * now a detailed check on message size + */ + CHECK_COMPLETE_SIZE (iceConn, global_data.major_opcode, opcode, + length, pData - pStart + SIZEOF (pmGetProxyAddrMsg), + pStart, IceFatalToProtocol); + + pData = pStart; + /* + * extract message data, based on known characteristics + * of this message type + */ + EXTRACT_STRING (pData, swap, serviceName); + EXTRACT_STRING (pData, swap, serverAddress); + EXTRACT_STRING (pData, swap, hostAddress); + EXTRACT_STRING (pData, swap, startOptions); + if (authLen > 0) + { + EXTRACT_STRING (pData, swap, authName); + authData = (char *) malloc (authLen); + memcpy (authData, pData, authLen); + } +#ifdef DEBUG + (void) fprintf (stderr, + "Got GetProxyAddr, serviceName = %s, serverAddr = %s\n", + serviceName, serverAddress); + (void) fprintf (stderr, + "\thostAddr = %s, options = %s, authLen = %d\n", + hostAddress, startOptions, authLen); + if (authLen > 0) + (void) fprintf (stderr, "\tauthName = %s\n", authName); +#endif + /* + * need to copy the host port string because strtok() changes it + */ + if ((tmp_str = strdup (serverAddress)) == NULL) + { + (void) fprintf(stderr, "malloc - serverAddress copy\n"); + goto sendFailure; + } + + /* + * before proceeding we want to verify that we are allowed to + * accept connections from the host who called xfindproxy(); + * the thing is, we don't get that host name from Proxy Manager + * even if the "-host <hostname>" command-line option was present + * in xfindproxy (and even if it was we shouldn't rely on it -- + * much better to have ProxyMngr query the xfindproxy connect + * socket for its origin); the upshot of all this that we do + * a configuration check *only* on the destination (which we + * assume in this case to be the serverAddress passed in by + * xfindproxy(); so get the destination IP address! + */ + server_name_base = strtok(tmp_str, ":"); + if ((hostptr = gethostbyname(server_name_base)) == NULL) + { + (void) fprintf(stderr, "gethostbyname (%s) failed\n", server_name_base); + goto sendFailure; + } + memset(&server_sockaddr_in, 0, sizeof(server_sockaddr_in)); + memset(&dummy_sockaddr_in, 0, sizeof(dummy_sockaddr_in)); + memcpy((char *) &server_sockaddr_in.sin_addr, + hostptr->h_addr, + hostptr->h_length); + + /* + * need to initialize dummy to something, but doesn't matter + * what (should eventually be the true host address); + * NOTE: source configuration will always match (see XFWP man + * page) unless sysadmin explicitly chooses to deny + */ + memcpy((char *) &dummy_sockaddr_in.sin_addr, + hostptr->h_addr, + hostptr->h_length); + + if ((doConfigCheck(&dummy_sockaddr_in, + &server_sockaddr_in, + global_data.config_info, + FINDPROXY, + &rule_number)) == FAILURE) + { + (void) fprintf(stderr, "xfindproxy failed config check\n"); + sendFailure: + /* + * report failure back to the ProxyMgr + * + */ + pm_send_msg_len = STRING_BYTES(config_failure) + + STRING_BYTES(NULL); + IceGetHeaderExtra(iceConn, + program_data->major_opcode, + PM_GetProxyAddrReply, + SIZEOF(pmGetProxyAddrReplyMsg), + WORD64COUNT (pm_send_msg_len), + pmGetProxyAddrReplyMsg, + pReply, + pReplyData); + pReply->status = PM_Failure; + STORE_STRING(pReplyData, NULL); + STORE_STRING(pReplyData, config_failure); + IceFlush(iceConn); + free(tmp_str); + return; + } + + /* + * okay, you got what you need from the PM to proceed, + * so extract the fd of the selected connection and use + * it to set up the remote client listen port and add + * the name of the X server to your list of server connections + */ + + /* + * Before checking to see if you already have a PM connection + * request for this server, make serverAddress a + * FQDN so that synonomous names like oregon:0 and oregon.com:0 + * will be recognized as the same Xserver. If this server + * already exists, don't allocate another listen port for it - + * use the already allocated one + */ + colon = strchr (serverAddress, ':'); + if (colon) + { + struct hostent *hostent; + + *colon = '\0'; + hostent = gethostbyname (serverAddress); + *colon = ':'; + + if (hostent && hostent->h_name) { + tmpAddress = (char *) malloc (strlen (hostent->h_name) + + strlen (colon) + 1); + (void) sprintf (tmpAddress, "%s%s", hostent->h_name, colon); + serverAddress = tmpAddress; + } + } + + if ((doCheckServerList(serverAddress, + &listen_port_string, + program_data->config_info->num_servers)) == FAILURE) + { + /* + * this server name isn't in your list; so set up a new + * remote client listen port for it; extract the fd from + * the connection and pass it in as index to array lookup + */ + if ((doSetupRemClientListen(&listen_port_string, + program_data, + serverAddress)) == FAILURE) + { + goto sendFailure; + } + } + if (tmpAddress) + free (tmpAddress); + + /* + * the PM-sent server address *was* in your list, so send back + * the rem client listen port you had already associated with + * that server (it will presumably be forwarded to the remote + * client through some other channel) + * use IceGetHeaderExtra() and the + */ + pm_send_msg_len = STRING_BYTES(listen_port_string) + + STRING_BYTES(NULL); + IceGetHeaderExtra(iceConn, + program_data->major_opcode, + PM_GetProxyAddrReply, + SIZEOF(pmGetProxyAddrReplyMsg), + WORD64COUNT (pm_send_msg_len), + pmGetProxyAddrReplyMsg, + pReply, + pReplyData); + pReply->status = PM_Success; + STORE_STRING(pReplyData, listen_port_string); + STORE_STRING(pReplyData, NULL); + IceFlush(iceConn); + /* + * before leaving this routine, change the select() timeout + * here to be equal to the configured client listen timeout + * (otherwise you'll never *get* to your listen timeout + * if it's shorter than the startup select() default + */ + program_data->config_info->select_timeout.tv_sec = + program_data->config_info->client_listen_timeout; + break; + } + + case ICE_Error: + { + iceErrorMsg *pMsg; + char *pStart; + + CHECK_AT_LEAST_SIZE (iceConn, global_data.major_opcode, ICE_Error, + length, sizeof(iceErrorMsg), IceFatalToProtocol); + + IceReadCompleteMessage (iceConn, SIZEOF (iceErrorMsg), + iceErrorMsg, pMsg, pStart); + + if (!IceValidIO (iceConn)) + { + IceDisposeCompleteMessage (iceConn, pStart); + return; + } + + if (swap) + { + pMsg->errorClass = lswaps (pMsg->errorClass); + pMsg->offendingSequenceNum = lswapl (pMsg->offendingSequenceNum); + } + + (void) fprintf(stderr, "Proxy Manager reported ICE Error:\n"); + (void) fprintf(stderr, "\tclass = 0x%x, offending minor opcode = %d\n", + pMsg->errorClass, pMsg->offendingMinorOpcode); + (void) fprintf(stderr, "\tseverity = %d, sequence = %d\n", + pMsg->severity, pMsg->offendingSequenceNum); + + IceDisposeCompleteMessage (iceConn, pStart); + + break; + } + + default: + break; + } /* end switch */ +} + + +/*ARGSUSED*/ +Bool +FWPHostBasedAuthProc ( + char * hostname) +{ + /* + * don't worry about config for now + * + * this routine gets called *after* IceAcceptConnection + * is called but *before* that routine returns its status; + * it is therefore the logical place to check configuration + * data on which PM connections (from which hosts) will be + * accepted; so do it and return either 0 to terminate + * connection (automatically informing PM) or 1 to proceed + * + * the PM host is not allowed; terminate connection and inform + * requestor why (handled automatically by ICElib) + */ + + return True; +} + + +/*ARGSUSED*/ +Status +FWPprotocolSetupProc( + IceConn iceConn, + int major_version, + int minor_version, + char * vendor, + char * release, + IcePointer * clientDataRet, + char ** failureReasonRet) +{ + /* + * This routine gets invoked when the remote ICE originator + * (in this case PM) calls IceRegisterSetup() on *its* end + * of the FWP connection; this is where the pointer to + * client data should be initialized, so that this data + * can be accessed when FMprocessMessages is called by + * IceProcessMessages() + */ + struct clientDataStruct * client_data; + if ((client_data = (struct clientDataStruct *) + malloc (sizeof (struct clientDataStruct))) == NULL) + { + (void) fprintf(stderr, "malloc - client data object\n"); + return (0); + } + /* + * setup the client data struct; we need this object in order to + * avoid making these variables global so they can be accessed in the + * ICE FWPprocessMessages() callback; now you see that our global_data + * struct was the only way of getting program data into the + * protocolReply setup routine! + */ + client_data->config_info = global_data.config_info; + client_data->nfds = global_data.nfds; + client_data->rinit = global_data.rinit; + client_data->winit = global_data.winit; + client_data->major_opcode = global_data.major_opcode; + *clientDataRet = client_data; + + return (1); +} + +int +doSetupPMListen( + char * pm_port, + int * size_pm_listen_array, + int ** pm_listen_array, + IceListenObj ** listen_objects, + int * nfds, + fd_set * rinit) +{ + int num_fds_returned; + char errormsg[256]; + int fd_counter; + IceListenObj * temp_obj; + + /* + * establish PM listeners + */ + if (!IceListenForWellKnownConnections(pm_port, + &num_fds_returned, + listen_objects, + 256, + errormsg)) + { + (void) fprintf(stderr, "IceListenForWellKnowConnections error: %s\n", + errormsg); + return 0; + } + + /* + * Create space for pm_listen_array + */ + *pm_listen_array = (int *) malloc (num_fds_returned * sizeof (int *)); + if (!pm_listen_array) + { + (void) fprintf (stderr, "malloc - pm_listen_array\n"); + return 0; + } + *size_pm_listen_array = num_fds_returned; + + /* + * obtain the PM listen fd's for the connection objects + */ + for (fd_counter = 0; fd_counter < num_fds_returned; fd_counter++) + { + /* + * get fd(s) for PM listen (could be more than one if different + * transport mechanisms) + */ + temp_obj = *listen_objects; + IceSetHostBasedAuthProc(temp_obj[fd_counter], FWPHostBasedAuthProc); + (*pm_listen_array)[fd_counter] = + IceGetListenConnectionNumber(temp_obj[fd_counter]); + + /* + * set all read mask bits on which we are going to select(); + * [NOTE: We don't care about write bits here because we don't + * use select() to manage writing to the PM] + */ + FD_SET((*pm_listen_array)[fd_counter], rinit); + + /* + * compute nfds for select() + */ + *nfds = max(*nfds, (*pm_listen_array)[fd_counter] + 1); + } + return 1; +} + + +/* + * The real way to handle IO errors is to check the return status + * of IceProcessMessages. xsm properly does this. + * + * Unfortunately, a design flaw exists in the ICE library in which + * a default IO error handler is invoked if no IO error handler is + * installed. This default handler exits. We must avoid this. + * + * To get around this problem, we install an IO error handler that + * does a little magic. Since a previous IO handler might have been + * installed, when we install our IO error handler, we do a little + * trick to get both the previous IO error handler and the default + * IO error handler. When our IO error handler is called, if the + * previous handler is not the default handler, we call it. This + * way, everyone's IO error handler gets called except the stupid + * default one which does an exit! + */ + +static IceIOErrorHandler prev_handler; + +static void +MyIoErrorHandler ( + IceConn ice_conn) + +{ + if (prev_handler) + (*prev_handler) (ice_conn); +} + +void +doInstallIOErrorHandler () +{ + IceIOErrorHandler default_handler; + + prev_handler = IceSetIOErrorHandler (NULL); + default_handler = IceSetIOErrorHandler (MyIoErrorHandler); + if (prev_handler == default_handler) + prev_handler = NULL; +#ifdef X_NOT_POSIX + signal(SIGPIPE, SIG_IGN); +#else + { + struct sigaction act; + + (void) sigemptyset(&act.sa_mask); + act.sa_flags = 0; + act.sa_handler = SIG_IGN; + (void) sigaction(SIGPIPE, &act, NULL); + } +#endif +} + @@ -0,0 +1,69 @@ +/* $Xorg: pm.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ +/* + +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. + +*/ + +#ifndef _PM_H +#define _PM_H + +extern void FWPprocessMessages( + IceConn iceConn, + IcePointer * client_data, + int opcode, + unsigned long length, + Bool swap); + +extern Bool +FWPHostBasedAuthProc ( + char * hostname); + +extern Status +FWPprotocolSetupProc( + IceConn iceConn, + int major_version, + int minor_version, + char * vendor, + char * release, + IcePointer * clientDataRet, + char ** failureReasonRet); + +extern int +doSetupPMListen( + char * pm_port, + int * size_pm_listen_array, + int ** pm_listen_array, + IceListenObj ** listen_objects, + int * nfds, + fd_set * rinit); + +extern void +doInstallIOErrorHandler (); + +#endif /* _PM_H */ diff --git a/transport.c b/transport.c new file mode 100644 index 0000000..864ab6e --- /dev/null +++ b/transport.c @@ -0,0 +1,334 @@ +/* $Xorg: transport.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ +/* + +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. + +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <errno.h> +#include <X11/Xos.h> +#include <netinet/in.h> +#include <sys/socket.h> +#include <netdb.h> + +#include <X11/Xfuncs.h> /* Need for bzero() */ + +#ifdef X_NOT_STDC_ENV +extern int errno; +#endif + +#include <X11/ICE/ICElib.h> /* Need typedef for Bool */ + +#include "xfwp.h" +#include "transport.h" + +int +doSetupRemClientListen( + char ** listen_port_string, + struct clientDataStruct * program_data, + char * server_address) +{ + int this_server; + int one = 1; + struct sockaddr_in rem_sockaddr_in; + int listen_port; + char hostname[MAX_HOSTNAME_LEN]; + struct timeval time_val; + struct timezone time_zone; + int num_servers = program_data->config_info->num_servers; + + /* + * ugh. This really shouldn't be kept as a sparse list but no time... + */ + for (this_server = 0; + this_server < num_servers && server_array[this_server] != NULL; + this_server++); + if (this_server == num_servers) + { + (void) fprintf(stderr, + "Maximum number of server connections has been reached (%d)\n", + program_data->config_info->num_servers); + return FAILURE; + } + + /* + * offset listen port into the X protocol range; + * must be > X_SERVER_PORT_BASE < 65535 + */ + listen_port = this_server + X_SERVER_PORT_BASE + 1; + + /* + * allocate the server_array struct and init the fd elements; + * can't use the PM connection fd as an index into this array, since + * there could be multiple servers per PM connection + */ + if ((server_array[this_server] = + (struct server_list *) malloc(sizeof(struct server_list))) == NULL) + { + (void) fprintf(stderr,"malloc - server_array\n"); + return FAILURE; + } + + if ((server_array[this_server]->client_listen_fd = + socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + (void) fprintf(stderr,"socket call failed\n"); + free(server_array[this_server]); + server_array[this_server] = NULL; + return FAILURE; + } + + /* + * this is where we initialize the current time and timeout on this + * client_listen object + */ + gettimeofday(&time_val, &time_zone); + server_array[this_server]->creation_time = time_val.tv_sec; + server_array[this_server]->time_to_close = + global_data.config_info->client_listen_timeout; + + /* + * set up the rest of the remote client listener + */ + bzero((char * ) &rem_sockaddr_in, sizeof(rem_sockaddr_in)); + rem_sockaddr_in.sin_family = AF_INET; +#ifdef BSD44SOCKETS + rem_sockaddr_in.sin_len = sizeof rem_sockaddr_in; +#endif + +#ifdef SO_REUSEADDR + if (setsockopt(server_array[this_server]->client_listen_fd, + SOL_SOCKET, SO_REUSEADDR, + (char *) &one, sizeof(int)) < 0) + { + (void) fprintf(stderr, "setsockopt(SO_REUSEADDR) failed\n"); + returnFailure: + close(server_array[this_server]->client_listen_fd); + free(server_array[this_server]); + server_array[this_server] = NULL; + return FAILURE; + } +#endif /* SO_REUSEADDR */ + + while (True) { + rem_sockaddr_in.sin_port = htons(listen_port); + if (bind(server_array[this_server]->client_listen_fd, + (struct sockaddr *)&rem_sockaddr_in, + sizeof(rem_sockaddr_in)) == 0) + break; + if (errno != EADDRINUSE) + { + (void) fprintf(stderr,"bind call failed\n"); + goto returnFailure; + } + listen_port++; + + /* + * Cann't keep going forever. + * + * Why 65535 - it's the same value used by the LBXProxy. + */ + if (listen_port > 65535) + { + (void) fprintf(stderr,"failed to find a valid port for bind\n"); + goto returnFailure; + } + } + +#ifdef DEBUG + (void) fprintf (stderr, "Client connect port: %d\n", listen_port); +#endif + + if (listen(server_array[this_server]->client_listen_fd, SOMAXCONN) < 0) + { + (void) fprintf(stderr, "listen call failed\n"); + goto returnFailure; + } + + /* + * update the nfds + */ + *(program_data->nfds) = max(*(program_data->nfds), + server_array[this_server]->client_listen_fd + 1); + + /* + * get fully qualified name of host on which FWP is running + */ + if ((gethostname(hostname, MAX_HOSTNAME_LEN)) < 0) + { + (void) fprintf(stderr, "gethostname call failed\n"); + goto returnFailure; + } + + /* + * allocate and convert the listen_port string for return to PM; + * string equals address of host on which FWP is running + * plus ":<listen_port - X_SERVER_PORT_BASE> (up to xxx)" + */ + if (((*listen_port_string) = + (char *) malloc (strlen(hostname) + 10)) == NULL) + { + (void) fprintf(stderr, "malloc - proxy address\n"); + goto returnFailure; + } + (void) sprintf (*listen_port_string, "%s:%d", hostname, + listen_port - X_SERVER_PORT_BASE); + + /* + * add the server name associated with the current PM request + * to the list + */ + if ((server_array[this_server]->x_server_hostport = + strdup (server_address)) == NULL) + { + (void) fprintf(stderr, "malloc - server_array string\n"); + free(*listen_port_string); + *listen_port_string = NULL; + goto returnFailure; + } + + /* + * add the client listen port associated with the current PM connection + * to the list + */ + if ((server_array[this_server]->listen_port_string = + strdup (*listen_port_string)) == NULL) + { + (void) fprintf(stderr, "malloc - server_array string\n"); + free(server_array[this_server]->x_server_hostport); + free(*listen_port_string); + *listen_port_string = NULL; + goto returnFailure; + } + + /* + * set the select() read mask for this descriptor + */ + FD_SET(server_array[this_server]->client_listen_fd, program_data->rinit); + + return SUCCESS; +} + +void +doSelect(struct + config * config_info, + int * nfds, + int * nready, + fd_set * readable, + fd_set * writable) +{ + if ((*nready = select(*nfds, + readable, + writable, + NULL, + &config_info->select_timeout)) == -1) + { + if (errno == EINTR) + return; + (void) fprintf(stderr, "select call failed\n"); + perror("select"); + exit(1); + } +} + +int +doServerConnectSetup( + char * x_server_hostport, + int * server_connect_fd, + struct sockaddr_in * server_sockaddr_in) +{ + struct hostent * hostptr; + char * server_name_base; + char server_port_base[10]; + int server_port; + int i = 0; + char * tmp_str; + char * tmp_hostport_str; + + /* + * need to copy the host port string because strtok() changes it + */ + if ((tmp_hostport_str = strdup (x_server_hostport)) == NULL) + { + (void) fprintf(stderr, "malloc - hostport copy\n"); + return FAILURE; + } + + tmp_str = x_server_hostport; + while (tmp_str[i] != ':') + tmp_str++; + tmp_str++; + strcpy(server_port_base, tmp_str); + server_name_base = strtok(tmp_hostport_str,":"); + server_port = atoi(server_port_base) + X_SERVER_PORT_BASE; + hostptr = gethostbyname(server_name_base); + free(tmp_hostport_str); + + if (hostptr == NULL) + { + (void) fprintf(stderr, "gethostbyname call failed\n"); + return FAILURE; + } + + if ((*server_connect_fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) + { + (void) fprintf(stderr, "socket call for server failed: %s\n", + strerror(errno)); + return FAILURE; + } + + memset(server_sockaddr_in, 0, sizeof(*server_sockaddr_in)); + server_sockaddr_in->sin_family = hostptr->h_addrtype; +#ifdef BSD44SOCKETS + server_sockaddr_in->sin_len = sizeof server_sockaddr_in; +#endif + memcpy((char *) &server_sockaddr_in->sin_addr, + hostptr->h_addr, + hostptr->h_length); + server_sockaddr_in->sin_port = htons(server_port); + + return SUCCESS; +} + +int +doServerConnect( + int * server_connect_fd, + struct sockaddr_in * server_sockaddr_in) +{ + if(connect(*server_connect_fd, (struct sockaddr * )server_sockaddr_in, + sizeof(*server_sockaddr_in)) < 0) + { + (void) fprintf(stderr, "connect call to server failed: %s\n", + strerror(errno)); + return FAILURE; + } + return SUCCESS; +} + diff --git a/transport.h b/transport.h new file mode 100644 index 0000000..99f3892 --- /dev/null +++ b/transport.h @@ -0,0 +1,70 @@ +/* $Xorg: transport.h,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ +/* + +Copyright "1986-1997, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and the following permission notice +shall be included in all copies of the Software: + +THE SOFTWARE IS PROVIDED "AS IS ", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE +AND NON-INFRINGEMENT. IN NO EVENT SHALL THE OPEN GROUP BE LIABLE +FOR ANY CLAIM, DAMAGES OR OTHER SIABILITIY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN +CONNNECTION WITH THE SOFTWARE OR THE USE OF OTHER DEALINGS IN +THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group +shall not be used in advertising or otherwise to promote the use +or other dealings in this Software without prior written +authorization from The Open Group. + +X Window System is a trademark of The Open Group. + +*/ + +#ifndef _TRANSPORT_H +#define _TRANSPORT_H + +#ifndef SOMAXCONN +#define SOMAXCONN 128 +#endif + +#define MAX_HOSTNAME_LEN 256 + +#define X_SERVER_PORT_BASE 6000 + + +extern int +doSetupRemClientListen( + char ** listen_port_string, + struct clientDataStruct * program_data, + char * server_address); + +extern void +doSelect(struct + config * config_info, + int * nfds, + int * nready, + fd_set * readable, + fd_set * writable); + +extern int +doServerConnectSetup( + char * x_server_hostport, + int * server_connect_fd, + struct sockaddr_in * server_sockaddr_in); + +extern int +doServerConnect( + int * server_connect_fd, + struct sockaddr_in * server_sockaddr_in); + +#endif /* _TRANSPORT_H */ @@ -0,0 +1,151 @@ +/* $Xorg: xfwp.c,v 1.4 2001/02/09 02:05:45 xorgcvs Exp $ */ + +/* +Copyright 1996, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <X11/Xos.h> +#include <netinet/in.h> + +#include <X11/ICE/ICElib.h> + +#include "xfwp.h" +#include "io.h" +#include "misc.h" +#include "pm.h" +#include "transport.h" + +/* + * Global variables + */ +struct clientDataStruct global_data; /* for ICE callbacks */ + +struct pm_conn_buf ** pm_conn_array; +struct server_list ** server_array; +struct client_conn_buf ** client_conn_array; + +char **SitePolicies = NULL; /* list of site security policy strings */ + +int SitePolicyCount = 0; /* count of elements in SitePolicies */ + +Bool SitePolicyPermit = 0; /* True := permit iff server supports + * at least one listed policy, + * False := deny if server has any of + * the listed policies. + */ + +int +main ( + int argc, + char * argv[]) +{ + int * pm_listen_array; + fd_set readable, writable, rinit, winit; + int nfds = 0; + int nready = 0; + struct ICE_setup_info pm_conn_setup; + IceListenObj * listen_objects; + struct config * config_info; + + /* + * setup the global client data struct; we need to do this in order + * to access program data in the ICE FWPprocessMessages() callback + * without making everything global! See FWPprotocolSetupProc() for + * the rest of what we are doing + */ + config_info = (struct config *) Malloc(sizeof(struct config)); + + global_data.config_info = config_info; + global_data.nfds = &nfds; + global_data.rinit = &rinit; + global_data.winit = &winit; + + /* + * complete the setup + */ + doProcessInputArgs(config_info, argc, argv); + + if ((doHandleConfigFile(config_info)) == FAILURE) + exit(1); + + if ((doInitDataStructs(config_info, + &pm_conn_setup)) == FAILURE) + exit(1); + + /* + * install the ICE i/o error handler + */ + doInstallIOErrorHandler(); + + /* + * zero the select() read/write masks + */ + FD_ZERO(&readable); + FD_ZERO(&writable); + FD_ZERO(&rinit); + FD_ZERO(&winit); + + /* + * create listener socket(s) for PM connections + */ + if (!doSetupPMListen(config_info->pm_listen_port, + &config_info->num_pm_listen_ports, + &pm_listen_array, + &listen_objects, + &nfds, + &rinit)) + exit(1); + + while(1) + { + readable = rinit; + writable = winit; + + doSelect (config_info, + &nfds, + &nready, + &readable, + &writable); + + doCheckTimeouts (config_info, + &nready, + &rinit, + &winit, + &readable, + &writable); + + doProcessSelect (&nfds, + &nready, + &readable, + &writable, + &rinit, + &winit, + pm_listen_array, + config_info, + &listen_objects); + } +} @@ -0,0 +1,318 @@ +/* $Xorg: xfwp.h,v 1.6 2001/02/09 02:05:46 xorgcvs Exp $ */ + +/* +Copyright 1996, 1998 The Open Group + +Permission to use, copy, modify, distribute, and sell this software and its +documentation for any purpose is hereby granted without fee, provided that +the above copyright notice appear in all copies and that both that +copyright notice and this permission notice appear in supporting +documentation. + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of The Open Group shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from The Open Group. +*/ + +#ifndef _XFWP_H +#define _XFWP_H + +#define FALSE 0 +#define TRUE 1 + +#define min(a,b) ((a) < (b) ? (a) : (b)) +#define max(a,b) ((a) > (b) ? (a) : (b)) + +/* + * Default connection array sizes + */ +#define MAX_PM_CONNS 10 +#define MAX_SERVERS 100 + +#define RWBUFFER_SIZE 2048 + +enum CLIENT_CONN_STATE { + CLIENT_WAITING, + SERVER_WAITING, + SERVER_REPLY, + CONNECTION_READY +}; + +enum PM_CONN_STATE { + START, + WAIT_SERVER_INFO, + SENT_PORT_INFO, + PM_EXCHANGE_DONE +}; + +enum CONFIG_CHECK { + FAILURE, + SUCCESS +}; + +enum CONFIG_TYPE { + PM, + REM_CLIENT +}; + +enum LISTEN_STATE { + AVAILABLE, + IN_USE +}; + +enum SERVICE_ID_TYPES { + CLIENT, + PMGR, + FINDPROXY +}; + +enum LOG_EVENTS { + CLIENT_ACCEPT, /* event 0: client connection granted */ + CLIENT_REJECT_CONFIG, /* event 1: client conn rejected by config file */ + CLIENT_REJECT_SERVER /* event 2: client conn rejected by server query */ +}; + +typedef void fp1(); +typedef Bool fp2(); + +struct ICE_setup_info +{ + + int opcode; + int versionCount; + IcePaVersionRec PMVersions[1]; + void (*fp1) (); + Bool (*fp2) (); +}; + +struct client_conn_buf +{ + char readbuf[RWBUFFER_SIZE]; + char writebuf[RWBUFFER_SIZE]; + int rbytes; + int wbytes; + int conn_to; + int wclose; + int state; + int time_to_close; + int creation_time; + int fd; + char * source; + char * destination; +}; + +struct pm_conn_buf +{ + char readbuf[RWBUFFER_SIZE]; + int rbytes; + int state; + int fd; + IceConn ice_conn; + int creation_time; + int time_to_close; +} ; + +struct config +{ + int num_client_conns; + int num_pm_conns; + int num_servers; + int num_pm_listen_ports; + int idle_timeout; + int pm_data_timeout; + int client_listen_timeout; + int client_data_timeout; + struct timeval select_timeout; + char * pm_listen_port; + char * config_file_path; + char * log_file_path; + int lines_allocated; + int rule_count; + struct config_line ** config_file_data; + int log_level; +}; + +struct server_list +{ + char * x_server_hostport; + char * listen_port_string; + int client_listen_fd; + int server_fd; + int done_accept; + int creation_time; + int time_to_close; +}; + +struct clientDataStruct +{ + int * nfds; + fd_set * rinit; + fd_set * winit; + int major_opcode; + struct config * config_info; +}; + +struct config_line +{ + char * permit_deny; + char * source_hostname; + unsigned int source_host; + char * source_netmask; + unsigned int source_net; + char * dest_hostname; + unsigned int dest_host; + char * dest_netmask; + unsigned int dest_net; +#if defined(__cplusplus) || defined(c_plusplus) + char * c_operator; +#else + char * operator; +#endif + char * service; + int service_id; +}; + + +/* + * Global variables + */ +extern struct clientDataStruct global_data; /* for ICE callbacks */ + +extern struct pm_conn_buf ** pm_conn_array; +extern struct server_list ** server_array; +extern struct client_conn_buf ** client_conn_array; + +extern char **SitePolicies; +extern int SitePolicyCount; +extern int SitePolicyPermit; + +/* + * Handy ICE message parsing macros + */ + +/* + * Pad to a 64 bit boundary + */ + +#define PAD64(_bytes) ((8 - ((unsigned int) (_bytes) % 8)) % 8) + +#define PADDED_BYTES64(_bytes) (_bytes + PAD64 (_bytes)) + + +/* + * Number of 8 byte units in _bytes. + */ + +#define WORD64COUNT(_bytes) (((unsigned int) ((_bytes) + 7)) >> 3) + + +/* + * Compute the number of bytes for a STRING representation + */ + +#define STRING_BYTES(_str) (2 + (_str ? strlen (_str) : 0) + \ + PAD64 (2 + (_str ? strlen (_str) : 0))) + + + +#define SKIP_STRING(_pBuf, _swap) \ +{ \ + CARD16 _len; \ + EXTRACT_CARD16 (_pBuf, _swap, _len); \ + _pBuf += _len; \ + if (PAD64 (2 + _len)) \ + _pBuf += PAD64 (2 + _len); \ +} + +/* + * STORE macros + */ + +#define STORE_CARD16(_pBuf, _val) \ +{ \ + *((CARD16 *) _pBuf) = _val; \ + _pBuf += 2; \ +} + +#define STORE_STRING(_pBuf, _string) \ +{ \ + int _len = _string ? strlen (_string) : 0; \ + STORE_CARD16 (_pBuf, _len); \ + if (_len) { \ + memcpy (_pBuf, _string, _len); \ + _pBuf += _len; \ + } \ + if (PAD64 (2 + _len)) \ + _pBuf += PAD64 (2 + _len); \ +} + + +/* + * EXTRACT macros + */ + +#define EXTRACT_CARD16(_pBuf, _swap, _val) \ +{ \ + _val = *((CARD16 *) _pBuf); \ + _pBuf += 2; \ + if (_swap) \ + _val = lswaps (_val); \ +} + +#define EXTRACT_STRING(_pBuf, _swap, _string) \ +{ \ + CARD16 _len; \ + EXTRACT_CARD16 (_pBuf, _swap, _len); \ + _string = (char *) malloc (_len + 1); \ + memcpy (_string, _pBuf, _len); \ + _string[_len] = '\0'; \ + _pBuf += _len; \ + if (PAD64 (2 + _len)) \ + _pBuf += PAD64 (2 + _len); \ +} + + +/* + * Byte swapping + */ + +/* byte swap a long literal */ +#define lswapl(_val) ((((_val) & 0xff) << 24) |\ + (((_val) & 0xff00) << 8) |\ + (((_val) & 0xff0000) >> 8) |\ + (((_val) >> 24) & 0xff)) + +/* byte swap a short literal */ +#define lswaps(_val) ((((_val) & 0xff) << 8) | (((_val) >> 8) & 0xff)) + + +#define CHECK_AT_LEAST_SIZE(_iceConn, _majorOp, _minorOp, _expected_len, _actual_len, _severity) \ + if ((((_actual_len) - SIZEOF (iceMsg)) >> 3) > _expected_len) \ + { \ + _IceErrorBadLength (_iceConn, _majorOp, _minorOp, _severity); \ + return; \ + } + + +#define CHECK_COMPLETE_SIZE(_iceConn, _majorOp, _minorOp, _expected_len, _actual_len, _pStart, _severity) \ + if (((PADDED_BYTES64((_actual_len)) - SIZEOF (iceMsg)) >> 3) \ + != _expected_len) \ + { \ + _IceErrorBadLength (_iceConn, _majorOp, _minorOp, _severity); \ + IceDisposeCompleteMessage (iceConn, _pStart); \ + return; \ + } + +#endif /* _XFWP_H */ diff --git a/xfwp.man b/xfwp.man new file mode 100644 index 0000000..b830d08 --- /dev/null +++ b/xfwp.man @@ -0,0 +1,389 @@ +.\" $Xorg: xfwp.man,v 1.4 2001/02/09 02:05:46 xorgcvs Exp $ +.\" Copyright 1996, 1998 The Open Group +.\" +.\" Permission to use, copy, modify, distribute, and sell this software and its +.\" documentation for any purpose is hereby granted without fee, provided that +.\" the above copyright notice appear in all copies and that both that +.\" copyright notice and this permission notice appear in supporting +.\" documentation. +.\" +.\" The above copyright notice and this permission notice shall be included +.\" in all copies or substantial portions of the Software. +.\" +.\" THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +.\" OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +.\" MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +.\" IN NO EVENT SHALL THE OPEN GROUP BE LIABLE FOR ANY CLAIM, DAMAGES OR +.\" OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +.\" ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +.\" OTHER DEALINGS IN THE SOFTWARE. +.\" +.\" Except as contained in this notice, the name of The Open Group shall +.\" not be used in advertising or otherwise to promote the sale, use or +.\" other dealings in this Software without prior written authorization +.\" from The Open Group. +.\" +.nh +.TH XFWP 1 "Release 6.4" "X Version 11" +.SH NAME +xfwp - X firewall proxy +.SH SYNOPSIS +.B xfwp +[option ...] +.PP +.SH COMMAND LINE OPTIONS +The command line options that can be specified are: +.PP +.TP 8 +.B \-cdt \fInum_secs\fP +Used to override the default time-to-close (604800 seconds) for xfwp client +data connections on which there is no activity (connections over which +X protocol is already being relayed by xfwp) +.PP +.TP 8 +.B \-clt \fInum_secs\fP +Used to override the default time-to-close (86400 seconds) for xfwp client +listen ports (ports on xfwp to which X clients first connect when trying to +reach an X server) +.PP +.TP 8 +.B \-pdt \fInum_secs\fP +Used to override the default time-to-close (3600 seconds) for Proxy Manager +connections on which there is no activity +.PP +.TP 8 +.B \-config \fIfile_name\fP +Used to specify the configuration the name of the configuration file +.PP +.TP 8 +.B \-pmport \fIport_number\fP +Used to override the default port address (4444) for proxy manager connections +.PP +.TP 8 +.B \-verify +Used to display the configuration file rule that was actually matched for +each service request +.PP +.TP 8 +.B \-logfile \fIfile_name\fP +Used to specify the name of a file where audit information should be logged. +The format of a logged entry is: time of day; event code; source IP address; +destination IP address; and configuration rule number. The event codes +are: "0" for a successful connection; "1" if a connection is denied because of +a configuration rule; and "2" if a connection is denied because of an +authorization failure. If the event code is "1", and a configuration file +is used, the configuration rule number is the line number of the +configuration file where the match was made (see the section +CONFIGURATION FILE for more information). If the event code is not "1", +or if no configuration file is used, the configuration rule number is "-1". +.PP +.TP 8 +.B \-loglevel \fI{0,1}\fP +Used to specify the amount of audit detail that should be logged. If "0", +all connections are logged. If "1", only unsuccessful connections are logged. +.PP +.TP 8 +.B \-max_pm_conns \fInum_connections\fP +Used to specify the maximum number of Proxy Manager connections. The +default is 10. +.PP +.TP 8 +.B \-max_pm_conns \fInum_connections\fP +Used to specify the maximum number of X server connections. The +default is 100. +.PP +.SH DESCRIPTION +The X firewall proxy (xfwp) is an application layer gateway proxy +that may be run on a network firewall host to forward X traffic +across the firewall. Used in conjunction with the X server Security +extension and authorization checking, xfwp constitutes a safe, simple, +and reliable mechanism both to hide the addresses of X servers located +on the Intranet and to enforce a server connection policy. Xfwp cannot +protect against mischief originating on the Intranet; however, when +properly configured it can guarantee that only trusted clients originating +on authorized external Internet hosts will be allowed inbound access to +local X servers. + +To use xfwp there must be an X proxy manager running in the local environment +which has been configured at start-up to know the location of the xfwp. +[NOTE: There may be more than one xfwp running in a local environment; +see notes below on load balancing for further discussion.] Using the +xfindproxy utility (which relays its requests through the proxy manager) +a user asks xfwp to allocate a client listen port for a particular X server, +which is internally associated with all future connection requests for that +server. This client listen port address is returned by the proxy manager +through xfindproxy. The xfwp hostname and port number is then passed +out-of-band (i.e., via a Web browser) to some remote X client, which will +subsequently connect to xfwp instead of to the target X server. + +When an X client connection request appears on one of xfwp's listen ports, +xfwp connects to the X server associated with this listen port and performs +authorization checks against the server as well as against its own configurable +access control list for requesting clients. If these checks fail, or if +the requested server does not support the X Security Extension, the client +connection is refused. Otherwise, the connection is accepted and all ensuing +data between client and server is relayed by xfwp until the client terminates +the connection or, in the case of an inactive client, until a configured +timeout period is exceeded. Xfwp is designed to block while waiting for +activity on its connections, thereby minimizing demand for system cycles. + +If xfwp is run without a configuration file and thus no sitepolicy is +defined, if xfwp is using an X server where xhost + has been run to turn +off host-based authorization checks, when a client tries to connect to +this X server via xfwp, the X server will deny the connection. If xfwp +does not define a sitepolicy, host-based authorization must be turned on +for clients to connect to an X server via the xfwp. +.PP +.SH INTEROPERATION WITH IP PACKET-FILTERING ROUTERS +The whole purpose of the xfwp is to provide reliable control over access +to Intranet X servers by clients originating outside the firewall. At +the present time, such access control is typically achieved by firewall +configurations incorporating IP packet-filtering routers. Frequently, +the rules for such filters deny access to X server ports (range 6000 - +6xxx) for all Intranet host machines. + +In order for xfwp to do its job, restrictions on access for ports 6001 - 6xxx +must be removed from the rule-base of the IP packet-filtering router. [NOTE: +xfwp only assigns ports in the range beginning with 6001; access to port +6000 on all Intranet hosts may continue to be denied.] This does not +mean the Intranet firewall will be opened for indiscriminate entry by X +clients. Instead, xfwp supports a fully configurable rule-based access +control system, similar to that of the IP packet-filter router itself. +Xfwp in effect adds another level of packet-filtering control which is +fully configurable and applies specifically to X traffic. See section +entitled CONFIGURATION FILE, below, for further details. +.PP +.SH INSTALLATION, SETUP AND TROUBLESHOOTING +Xfwp is typically run as a background process on the Intranet firewall host. +It can be launched using any of the command-line options described above. +As noted above, xfwp works only in conjunction with proxy manager and the +xfindproxy utility. It can also be configured to support a user-defined +X server site security policy, in which the X server is required to indicate +to xfwp whether or not it supports the particular policy. Consult the +X server man pages for further information on these components. Xfwp +diagnostics can be turned on by compiling with the -DDEBUG switch. +Connection status can be recorded by using the -logfile and -loglevel +command line options. +.PP +.SH PERFORMANCE, LOAD BALANCING AND RESOURCE MANAGEMENT +Xfwp manages four different kinds of connections: proxy manager (PM) data, +X client listen, X client data, and X server. The sysadmin employing xfwp +must understand how the resources for each of these connection types are +allocated and reclaimed by xfwp in order to optimize the availability of +xfwp service. + +Each connection-type has a default number of allocation slots and +a default timeout. The number of allocation slots for PM connections +and X server connections is configurable via command line options. +Connection timeouts are also configurable via command line options. +Each connection timeout represents the period the connection +will be allowed to remain open in the absence of any activity on that +connection. Whenever there is activity on a connection, the time-to-close +is automatically reset. The default distribution of total process connection +slots across the four connection types, as well as the choice of default +timeouts for the connection types, is governed by a number of assumptions +embedded in the xfwp use model. + + +The default number of PM connections is 10 and the +default duration for PM connections is 3,600 +seconds (1 hour) for each connection after time of last activity. +At start-up, xfwp listens for PM connection requests on any non-reserved +port (default of 4444 if not specified on the xfwp command-line). The PM +normally connects to xfwp only when a call is made to the PM with xfindproxy. +Thereafter, the PM remains connected to xfwp, even after the messaging between +them has been completed, for the default connection duration period. In some +cases this may result in depletion of available PM connection slots. +If the sysadmin expects connections to a single xfwp from many PM's, +xfwp should be started using the -pdt command line option, with a timeout +value reflecting the desired duration that inactive connections will be +permitted to remain open. + +Xfwp client listeners are set up by a call to xfindproxy and continue to +listen for X client connection requests for a default duration of 86,400 +seconds (24 hours) from the point of last activity. After this time they +are automatically closed and their fd's recovered for future allocation. +In addressing the question of how to choose some alternative timeout +value which will guarantee the availability of client listen ports, +sysadmins should take into consideration the expected delay between +the time when the listener was allocated (using xfindproxy) and the time +when a client actually attempts to connect to xfwp, as well the likelihood +that client listeners will be re-used after the initial client data +connection is closed. + +Each client connection is allocated a default lifetime of 604,800 +seconds (7 * 24 hours) +from the point when it last saw activity. After this time it is +automatically closed and its fd's recovered for future allocation. +Because server connections are not actually established until a connection +request from a remote X client arrives at one of the xfwp's client listen +ports, the client data timeout applies both to client-xfwp connections as well +as to xfwp-server connections. If the system administrator expects many +client data connections through xfwp, an overriding of the default timeout +should be considered. +.PP +.SH CONFIGURATION FILE +The xfwp configuration file resides on the xfwp host machine and is +used to determine whether X client data connection requests will be +permitted or denied. The path to the file is specified at start-up +time. If no configuration file is specified, all X client data +connection requests routed through xfwp will be by default permitted, +assuming that other X server authorization checks are successful. If +a configuration file is supplied but none of its entries matches the +connection request then the connection is by default denied. + +If a line in the configuration file begins with the '#' character +or a new-line character, the line is ignored and the evaluator will +skip the line. + +The configuration file supports two entirely independent authorization +checks: one which is performed by xfwp itself, and a second which is the +result of xfwp's querying the target X server. For the first of these, +the configuration file employs a syntax and semantic similar to that of IP +packet-filtering routers. It contains zero or more source-destination +rules of the following form: +.PP +{permit | deny} <src> <src mask> [<dest> <dest mask> [<operator> <service>]] +.sp +.IP permit/deny 12 +the keywords ``permit'' or ``deny'' indicate whether the +rule will enable or disable access, respectively +.IP src 12 +the IP address against the host who originated the +connection request will be matched, expressed in IP +format (x.x.x.x) +.IP "src mask" 12 +a subnet mask, also in IP format, for further qualifying +the source mask. Bits set in the mask indicate bits of the +incoming address to be \fIignored\fP when comparing to the specified src +.IP dest 12 +the IP address against which the destination of the +incoming connection request (i.e. the host IP of the +X server to which the incoming client is attempting to +connect) will be matched +.IP "dest mask" 12 +a subnet mask, also in IP format, for further qualifying +the destination mask. Bits set in the mask indicate bits of the +destination address to be \fIignored\fP when comparing to the specified dest +.IP operator 12 +always ``eq'' (if the service field is not NULL) +.IP service 12 +one of the following three strings: ``pm'', ``fp'', or +``cd'', corresponding to proxy manager, xfindproxy, or +client data, respectively +.PP +For the second type of authorization check, the configuration file contains +zero or more site policy rules of the following form: +.PP +{require | disallow} sitepolicy <site_policy> +.sp +.IP require 12 +specifies that the X server \fImust\fP be configured with \fIat least one\fP +of the corresponding site policies, else it must refuse the connection. +.IP disallow 12 +specifies that the X server \fImust not\fP be configured with \fIany\fP of +the corresponding site policies, else it must refuse the connection. +.IP sitepolicy 12 +a required keyword +.IP "<site_policy>" 12 +specifies the policy string. The string may contain any +combination of alphanumeric characters subject +only to interpretation by the target X server +.PP +.SH RULES FOR EVALUATING THE XFWP CONFIGURATION FILE ENTRIES +For the first type of configurable authorization checking, access +can be permitted or denied for each connection type based upon +source and, optionally, destination and service. Each file entry must +at a minimum specify the keyword ``permit'' or ``deny'' and the two +source fields. The +destination and service fields can be used to provide finer-grained +access control if desired. +.PP +The algorithm for rule-matching is as follows: +.PP +.RS 3 + while (more entries to check) + { + if ((<originator IP> AND (NOT <src mask>)) == src) + [if ((<dest X server IP> AND (NOT <dest mask>)) == dest)] + [if (service fields present and matching)] + do either permit or deny connection depending on keyword + else + continue + } + if (no rule matches) + deny connection +.RE +.PP +If a permit or deny rule does not specify a service and operation, then +the rule applies to all services. If a configuration file is specified +and it contains at least one valid deny or permit rule, then a host +that is not explicitly permitted will be denied a connection. +.PP +Site policy configuration checking constitutes a separate (and X server +only) authorization check on incoming connection requests. Any number of +require or disallow rules may be specified, but all rules must be of the +same type; that is, a single rule file cannot have both ``require'' and +``disallow'' keywords. The algorithm for this check is as follows: +.PP +.RS 3 + if (X server recognizes any of the site policy strings) + if (keyword == require) + permit connection + else + deny connection + else + if (keyword == require) + deny connection + else + permit connection +.RE +.PP +The site policy check is performed by xfwp only if the source-destination +rules permit the connection. +.PP +.SH +EXAMPLES +.PP +.sp +\fC +.nf +# if and only if server supports one of these policies then authorize +# connections, but still subject to applicable rule matches +# +require sitepolicy policy1 +require sitepolicy policy2 +# +# deny pm connections originating on 8.7.6.5 [NOTE: If pm service +# is explicitly qualified, line must include destination fields as +# shown.] +# +deny 8.7.6.5 0.0.0.0 0.0.0.0 255.255.255.255 eq pm +# +# permit xfindproxy X server connects to anywhere [NOTE: If +# fp service is explicitly qualified, line must include source fields +# as shown.] +# +permit 0.0.0.0 255.255.255.255 0.0.0.0 255.255.255.255 eq fp +# +# permit all connection types originating from the 192.0.0.0 +# IP domain only +# +permit 192.0.0.0 0.255.255.255 +.fi +\fP +.PP +Care should be taken that source-destination rules are written in the correct +order, as the first matching rule will be applied. In addition to parser +syntax checking, a special command-line switch (-verify) has been provided +to assist the sysadmin in determining which rule was actually matched. +.PP +.SH BUGS +.PP +Xfwp should check server site policy and security extension before +allocating a listen port. +.PP +.SH SEE ALSO +xfindproxy (1), Proxy Management Protocol spec V1.0, proxymngr(1), Xserver(1) +.SH AUTHOR +Reed Augliere, consulting to X Consortium, Inc. |