Trusted Solaris Developer's Guide

Client-Server Application

This section presents a short client-server application using Berkeley sockets and the TSIX library to transfer data and security attribute information across the network. The communication path is connection-oriented using the internet domain (TCP/IP). The server is a concurrent process that supplies information about upcoming meetings at different sensitivity levels. To get the service, the client connects to the server and requests the information for a specified sensitivity level.

TCP/IP Server

The server process uses the net_mac_read privilege to bind to a multilevel port to serve single-level clients at different sensitivity levels. Chapter 10, Interprocess Communications describes multilevel and single-level ports.

The msg_array structure contains meeting information at Confidential, Secret, Top Secret, and NULL. To respond to a single-level client, the server process needs the proc_set_sl privilege in its effective set to change the sensitivity label of its child to be the same as the client.

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>

#include <tsol/label.h>
#include <tsix/t6attrs.h>

struct msg {
	char *sl;
	bslabel_t *bsl;
	char *msg;
} msg_array[] = {
	{"CONFIDENTIAL", NULL, "Staff Meeting at 1:00 pm, Rm 200"},
	{"SECRET", NULL, "Manager Meeting at 10:00 am, Rm 303"},
	{"TOP SECRET", NULL, "Exective Meeting at 3:00 pm, Rm 902"},
	{NULL, NULL, NULL};
};

This first part of the main program sets the process clearance to ADMIN_HIGH so the child process can set its sensitivity label to the sensitivity label of the requesting client. The proc_setclr privilege is needed for this task.

The code comments indicate where privilege bracketing as described in Chapter 3, Privileges should take place. With privilege bracketing, the net_reply_equal privilege should be off so the server can reply to the client at the sensitivity label specified by the msg_array data and not the sensitivity label of the requesting client. The code comments show at what point the net_repy_equal privilege must be off for the example to work.

main(int argc, char **argv)
{
	int fd, newfd, chpid, index, error;
	struct sockaddr_in serv_addr;
	bclear_t clearance;

	if (argc != 2) {
		printf("Usage: %s host\n", argv[0]);
		exit(1);
	}
	printf("PID = %ld\n", getpid());

	/* Set the process clearance to ADMIN_HIGH
	/* Turn the proc_setclr privilege on in the effective set */

	bclearhigh(&clearance);
	if (setclearance(&clearance) != 0) {
		perror("setclearance");
		exit(1);
	}
	/* Turn the proc_setclr privilege off */

This next main program segment creates binary sensitivity label from the data in msg_array. The binary labels are used later with the TSIX library routines.

	/* Obtain binary labels for run time efficiency */

	index = 0;
	while (msg_array[index].sl != NULL) {
		if ((msg_array[index].bsl =
			(bslabel_t *) malloc(sizeof (bslabel_t))) == NULL) {
			printf("No memory");
			exit (1);
		}
		if (stobsl(msg_array[index].sl, msg_array[index].bsl,
			NEW_LABEL, &error) != 1) {
			printf("converting SL %s failed\n",
			msg_array[index].sl);
			exit(1);
		}
		index++;
	}

This next main program segment sets up endpoint communications by creating a socket, binding it to a name, and listening on the socket for client requests. The code comments indicate where privilege bracketing as described in Chapter 3, Privileges should take place.

	if ((fd = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		exit(1);
	}
	memset(&serv_addr, 0, sizeof (serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_addr.s_addr = htonl(INADDR_ANY);
	serv_addr.sin_port = htons(10000);

/* Turn net_mac_read on in the effective set */
	if (bind(fd, (struct sockaddr *) &serv_addr,
		sizeof (serv_addr)) < 0) {
		perror("bind");
		exit(1);
	}
/* Turn the net_mac_read privilege off */

	listen(fd, 5);

The while loop accepts client connections on the socket and forks a process to handle each client request. The forked process prepares structures to receive the incoming message and its sensitivity label, and to set the sensitivity label portion of the process CMW label to the incoming sensitivity label. It also allocates handle_in with enough space to receive the sensitivity label on the incoming message, allocates handle_out with enough space to send a sensitivity label with the outgoing message, and receives the message and security attribute information with the t6recvfrom(3NSL) routine.

	while (1) {
		if ((newfd = accept(fd, NULL, 0)) < 0) {
			perror("accept");
			exit(1);
		}
		printf("Request Received\n");
		if ((chpid = fork()) < 0) {
			perror("fork");
			exit(1);
		} else if (chpid == 0) {        /* child process */
			t6attr_t handle_in;
			t6attr_t handle_out;
			t6mask_t mask_in = T6M_SL;
			int client_index = 0;
			bslabel_t *bsl;
			bclabel_t bcmwlabel;
			char buf[256];
			int index, buflen = 256;
			t6mask_t new_mask = T6M_NO_ATTRS;
			char *string = (char *) 0;
			char any;

			close(fd);
			printf("child PID = %ld\n", getpid());

/* Process client request */
			if ((handle_in = t6alloc_blk(mask_in)) == NULL) {
				printf("t6attr_alloc: no memory");
				exit(1);
			}
			if (t6recvfrom(newfd, buf, buflen, 0, 0, 0,
				handle_in, &new_mask) < 0) {
				perror("t6recvfrom");
				exit(1);
			}

The last main program segment extracts the sensitivity label received, sets the sensitivity label of the process to that of the client, and sends the reply to the client. The code comments indicate where privilege bracketing as described in Chapter 3, Privileges should take place.

	/* Get sensitivity label */
		if ((bsl = (bslabel_t *) t6get_attr(T6_SL,
				handle_in)) == NULL) {
				printf("t6get_attr: no SL available");
				exit(1);
		}
		if (bsltos(bsl, &string, 0, LONG_WORDS) < 0) {
				perror("bsltos");
				exit(1);
		}
		printf("Requestor's SL = %s\n", string);

	/* Set the sensitivity label of the child process to */
	/* that of the client */
		if (getcmwplabel(&bcmwlabel) != 0) {
				perror("getcmwplabel");
				exit(1);
		}
		setcsl(&bcmwlabel, bsl);

	/* Turn proc_set_sl on in the effective set */
		if (setcmwplabel(&bcmwlabel, SETCL_SL) < 0) {
				perror("setcmwplabel");
				exit (1);
		}
	/* Turn the proc_set_sl privilege off */

	/*
	 * Retreive the msg_array entry that matches the sensitivity
	 * label of the client.
	 */
		while (msg_array[client_index].sl != NULL) {
			if (blequal(bsl, msg_array[client_index].bsl)
			break;
			client_index++;
		}
		if (msg_array[client_index].sl == NULL) {
			perror("No message for the client's SL");
			exit (1);
		}
		send(new_fd, msg_array[client_index].msg,
			strlen(msg_array[client_index].msg));

		exit (0);

TCP/IP Client

To request the service, the client program connects to the server, sends a request, and waits for the meeting message. If the connection is closed before a message is received, the client exits because there is no meeting at its sensitivity label. If a message is received, the client uses t6recvfrom(3NSL) to obtain the message. Code to process the information is not shown in the example.

This first part of the program sets up data structures for the client request and server response.

#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <string.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <netdb.h>
#include <tsol/label.h>
#include <tsix/t6attrs.h>

char *clnt_req = "Request Meeting Info";

main(int argc, char **argv)
{
	int sock, retval;
	char buf[256];
	int buflen = 256;
	int num;
	struct sockaddr_in serv_addr;
	struct hostent *hostent;
	bslabel_t *bsl;
	t6mask_t new_mask, sl_mask = T6M_SL;
	t6attr_t handle;
	char    *string = (char *)0;

This next main program segment processes the command-line argc and argv inputs to get the host name and port number of the server and establishes a connection.

	if (argc != 2) {
		printf("Usage: %s host\n", argv[0]);
		exit (1);
	}
	if ((hostent = gethostbyname(argv[1])) == NULL) {
		perror("gethostbyname");
		exit(1);
	}

	memset((void *) &serv_addr, 0, sizeof (serv_addr));
	serv_addr.sin_family = AF_INET;
	serv_addr.sin_port = htons(10000);
	memcpy((void *) &serv_addr.sin_addr,
		(void *) hostent->h_addr_list[0], hostent->h_length);

	if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
		perror("socket");
		exit(1);
	}
	if (connect(sock, (struct sockaddr *)&serv_addr,
		sizeof (serv_addr)) < 0) {
		perror("connect");
		exit(1);
	}
	printf("Connected\n");
	if ((handle = t6alloc_blk(sl_mask)) == NULL) {
		printf("t6attr_alloc: no memory");
		exit(1);
	}

This next main program segment sends the request to the server. The request is sent at the sensitivity label at which the client process is executing. When the server processes the request, it sends back meeting information for the sensitivity label at which the request is made only. The t6recvfrom(3NSL) routine receives the meeting information.

/* Send a request to server */
	write(sock, clnt_req, strlen(clnt_req));

	if ((num = t6recvfrom(sock, buf, buflen, 0, 0, 0, handle,
		&new_mask)) < 0) {
		perror("t6recvfrom");
		exit (1);
	} else if (num == 0) {
		printf("Connection closed, nothing matches.\n");
		exit(0);
	} else
		printf("Received Reply\n");

	retval = bsltos(bsl, &string, 0, LONG_WORDS);
	printf("Retval = %d, Sensitivity label = %s\n", retval, string);
	printf("Message = %s\n", buf);
}