Silicon Graphics, Inc.
OpenVault
Interprocess Communication
and
Authentication
Design Document

$Id: commlib.html,v 1.13 1997/07/01 21:38:29 curtis Exp $

Overview/High Level Goals

OpenVault interprocess communication requires both the OpenVault server and clients to be able to exchange information. Typically, clients will make requests of the server to perform some service and the server will send one or more replies indicating the disposition of the client's request. The server will also need to send information asynchronously to the client representing status information, revocation information, and possibly additional information in the future.

OpenVault authentication requires that there be a mechanism in place to authenticate the OpenVault client to the OpenVault server and vice versa. Without such a mechanism, a potential attacker could insert rogue processes pretending to be legitimate clients or servers and thus subvert the integrity of the system and the data which it serves. OpenVault authentication is purposely limited to client and server authentication only; no user authentication is performed by the OpenVault server.


Decisions/Assumptions/Limitations

OpenVault is initially targeted to run on SGI hardware running Irix 5.3 and later operating systems. However, no limitations have been placed on the design which would prevent it from being ported to other POSIX- compliant implementations of Unix with a minimum of effort. Ports to operating systems other than Unix may be possible, but their consideration has been given a low priority in the current design.

From the standpoint of security and authentication, there is much to be said in favor of using an established authenticated key exchange mechanism combined with strong encryption in order to secure communications between the OpenVault server and its clients. However, due to anachronistic U.S. export laws regarding cryptography, such an implementation would be a sure route to a U.S.-only product. Since SGI does a great deal of international business, this was judged to be a poor compromise. It is still possible to secure the benefits of authentication and prevent a potential attacker from masquerading as an OpenVault process by using message integrity codes. Although all OpenVault communications will have to be clear (e.g. unencrypted), this allows the possibility of remaining within the limits of U.S. export law.


Functional Specification

Interprocess Communication

Since OpenVault relies on a client/server model, some means must be provided for clients to communicate with the server. This is where interprocess communications requirements originate. There must exist a simple client interface to allow clients to make requests of the server in either a synchronous or asynchronous manner. Furthermore, some provision must be made for the server to communicate information asynchronously to the client. Since clients and servers will initially be running on Unix-based machines connected by a network, the requirements for interprocess communications can be further refined to specify the use of a generic Unix network communications interface at the lowest level.

Authentication

Communications channels must be authenticated and message integrity assured. There must be a high level of confidence that a particular communications channel originates with a particular host. Further, there must be a high level of confidence that the data that flows over the communications channel has not been altered by any intervening hosts while enroute to its destination.


Implementation


    Client Side                             Server Side

    +--------------------+                  +--------------------+
    | Client Application |<-OpenVault cmds->| Server Functions   |
    +--------------------+                  +--------------------+
              |                                       |
    +--------------------+                  +--------------------+
    | Authentication     |<---ov layer----->| Authentication     |
    +--------------------+                  +--------------------+
              |                                       |
    +--------------------+                  +--------------------+
    | Comm Layer         |<---mlm layer--->| Comm Layer         |
    +--------------------+                  +--------------------+

    Figure 1 -- OpenVault Client/Server Interprocess Communication

Overview

There are three logical layers to the client side as illustrated in Figure 1 above. The top layer is the application layer. At this level, OpenVault commands are exchanged between the client and the server. The next layer is the OpenVault session layer and is implemented in the libov.a library. This layer handles mutual authentication, key management, and message integrity checking if it is enabled. The bottom layer is the OpenVault transport layer and is also implemented in the libov.a library. This layer handles the transport of raw messages between the client and the server via operating system calls.

Client Application Layer

The KISS principle was used in the design of the client application layer. Client applications need only concern themselves with six library calls to handle all of their communications needs with the server. A list of the functions follows: ov_open(), ov_send(), ov_recv(), ov_close(), ov_perror(), and free_result(). These functions are all implemented in the OpenVault client library, libov.a. Applications wishing to use these functions must link with this library and include the ov_lib.h include file. An overview of these functions follows.
ov_handle_t *ov_open(char *app_name, char *hostid, int flags)
This function takes three arguments. The first is a string containing the name of the application. The second is a string containing the hostname (or hostid) of the host on which the OpenVault server is running. The third argument is a flags argument. The function returns a pointer to a ov_handle_t structure. This structure contains opaque internal state information as well as several user accessible values including an integer and string with status information.

ov_send(ov_handle_t *handle, char *command, int timeout)
This function takes three arguments. The first is a pointer to a ov_handle_t structure which must have previously been returned by an earlier call to ov_open(). The second argument is a string containing ASCII commands to be send to the OpenVault server. The third argument is an integer timeout value. ov_send() returns an integer error value which contains zero if there was no error.

result_t *ov_recv(ov_handle_t *handle, int timeout)
This function is used to receive commands from the OpenVault server. It takes two arguments, a pointer to a ov_handle_t structure which must have been returned by a previous call to ov_open() and an integer timeout value. ov_recv() returns a ov_result_t structure. This function returns any pending message from the OpenVault server of if there are none waits up to the specified number of seconds for a message from the server before returning a timeout error. If successful, the ov_result_t structure will contain an ASCII message in it as well as a status integer with a value of OV_STATUS_OK (0). Otherwise, the status integer will be set to a negative value set to be equal to the type of error encountered.

ov_close(ov_handle_t *handle)
This function takes one argument. It is a pointer to a ov_handle_t structure which must have been returned by an earlier call to ov_open(). ov_close() frees the resources allocated by the ov_open() command and returns an integer error value which contains OV_STATUS_OK if there was no error, otherwise a negative error value.

void ov_perror(char *s, int err)
This function takes two arguments. The first is a string which is used to generate a message of the form : . The second is a negative error value returned by any of the OpenVault library calls. The function will generate an error message corresponding to the type of error represented by the second argument and print it to the standard error device.

void free_result(result_t *result)
This function is used to free the result_t structure returned by the ov_recv() function. The ov_recv() function allocates memory to hold the result_t structure it returns. In order to free the memory used by this structure, this function should be called with a pointer to the result_t structure to be freed as the single argument.

Authentication Layer

Initialization

Authentication occurs once a communications channel has been established between the initiator and the recipient. For illustrative purpose, we're going to assume that Alice represents the process that initiates communications with the OpenVault server (e.g. a client application, admin application, DCP, or LCP). Bob will represent the OpenVault server.

The authentication process begins with Alice sending her name to Bob. Bob replies by generating a 32-bit random number (R1) and sending it to Alice as a challenge. Upon receiving this number, Alice encrypts it with the key she shares with Bob and sends this value, along with another 32-bit random number she has generated herself (R2) to Bob. After checking to make sure that Alice has successfully encrypted R1, Bob then encrypts R2 and generates a third random number (R3). Bob now sends the encrypted R2 and encrypted R3 to Alice. Alice verifies that R2 has been properly encrypted and then decrypts R3 and stores it as the session key.

Command execution

If Alice wants to send a message to Bob, she must send him two values. The first value is the message itself. The second value is the Message Integrity Code (MIC) for the message. The MIC is generated by using a cryptographically strong hash algorithm (such as MD5) to hash the message and then encrypting the resulting hash with the session key obtained during the authentication phase.

Upon receiving Alice's request, Bob computes the MIC of the message string, encrypts it with the session key, and compares it to the MIC received with the message string. If the two values match, message integrity is assured.

In order to increase the resistance of the above paradigm to a type of attack known as a playback attack, the session key could be incremented by one (or otherwise modified in a deterministic fashion) after each successful transaction at both Alice's end and Bob's end.

Client Programming Example


#include 
#include 

#define OV_HOST		"openvault.engr.sgi.com"
#define MYAPPNAME	"name_of_my_application"

main(int argc, char *argv[])
{
    int status;
    ov_handle_t *handle;
    result_t *result;

    /*
     * Initiate communication with the server running on
     * the host specified by OV_HOST.  Register under application
     * name specified by MYAPPNAME.  Authentication is performed
     * transparently to the client.  A handle is returned which
     * containes opaque data used by the OpenVault client interface
     * library in addition to an error value and reply string.
     */
    handle = ov_open(MYAPPNAME, OV_HOST, 0);
    if (handle->status != OV_STATUS_OK) {
        ov_perror("ov_open", handle->status);
        exit(1);
    }

    /*
     * Send a test message to the server.
     */
    if ((status = ov_send(handle, "test 1", 5)) != OV_STATUS_OK) {
        ov_perror("ov_send", status);
        exit(1);
    }

    /*
     * Look for a reply from the server for up to 5 seconds.
     * Display the reply if we get it in time.
     */
    result = ov_recv(handle, 5);
    if (result->status != OV_STATUS_OK) {
        ov_perror("ov_recv", result->status);
        exit(1);
    }
    printf("Received reply \"%s\"\n", result->result);
    free_result(result);

    /*
     * Send a test message to the server.
     */
    if ((status = ov_send(handle, "test 2", 5)) != OV_STATUS_OK) {
        ov_perror("ov_send", status);
        exit(1);
    }
    
    /*
     * Look for a reply from the server for up to 5 seconds.
     * Display the reply if we get it in time.
     */
    result = ov_recv(handle, 5);
    if (result->status != OV_STATUS_OK) {
        ov_perror("ov_recv", result->status);
        exit(1);
    }
    printf("Received reply \"%s\"\n", result->result);
    free_result(result);

    /*
     * When we are finished issuing requests to the server, we
     * should call the ov_close() function to free system resources
     * allocated by the earlier call to ov_open().
     */
    ov_close(handle);

    exit(0);
}


Interfaces

Internal Interfaces

None.

External Interfaces

There are two external interfaces for the OpenVault IPC library. The client side interface, represented by the ov_*() calls described above, and the OpenVault server interface. The OpenVault server interface is currently implemented by TCP domain sockets. This provides a reliable streams-based interface between OpenVault client processes and the OpenVault server.