JavaScript is required to for searching.
Skip Navigation Links
Exit Print View
Developer's Guide to Oracle Solaris 11 Security     Oracle Solaris 11.1 Information Library
search filter icon
search icon

Document Information

Preface

1.  Oracle Solaris Security for Developers (Overview)

2.  Developing Privileged Applications

3.  Writing PAM Applications and Services

4.  Writing Applications That Use GSS-API

5.  GSS-API Client Example

6.  GSS-API Server Example

GSSAPI Server Example Overview

GSSAPI Server Example Structure

Running the GSSAPI Server Example

GSSAPI Server Example: main() Function

Acquiring Credentials

Checking for inetd

Receiving Data From a Client

Accepting a Context

Unwrapping the Message

Signing and Returning the Message

Using the test_import_export_context() Function

Cleanup in the GSSAPI Server Example

7.  Writing Applications That Use SASL

8.  Introduction to the Oracle Solaris Cryptographic Framework

9.  Writing User-Level Cryptographic Applications

10.  Introduction to the Oracle Solaris Key Management Framework

A.  Secure Coding Guidelines for Developers

B.  Sample C-Based GSS-API Programs

C.  GSS-API Reference

D.  Specifying an OID

E.  Source Code for SASL Example

F.  SASL Reference Tables

Glossary

Index

Receiving Data From a Client

After checking for inetd, the gss-server program then calls sign_server(), which does the main work of the program. sign_server() first establishes the context by calling server_establish_context().

sign_server() performs the following tasks:

These tasks are described in the subsequent sections. The following source code illustrates the sign_server() function.


Note - The source code for this example is also available through the Oracle download center. See http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html.


Example 6-3 sign_server() Function

int sign_server(s, server_creds)
     int s;
     gss_cred_id_t server_creds;
{
     gss_buffer_desc client_name, xmit_buf, msg_buf;
     gss_ctx_id_t context;
     OM_uint32 maj_stat, min_stat;
     int i, conf_state, ret_flags;
     char    *cp;
     
     /* Establish a context with the client */
     if (server_establish_context(s, server_creds, &context,
                  &client_name, &ret_flags) < 0)
    return(-1);
      
     printf("Accepted connection: \"%.*s\"\n",
        (int) client_name.length, (char *) client_name.value);
     (void) gss_release_buffer(&min_stat, &client_name);

     for (i=0; i < 3; i++)
         if (test_import_export_context(&context))
             return -1;

     /* Receive the sealed message token */
     if (recv_token(s, &xmit_buf) < 0)
    return(-1);
      
     if (verbose && log) {
    fprintf(log, "Sealed message token:\n");
    print_token(&xmit_buf);
     }

     maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf,
               &conf_state, (gss_qop_t *) NULL);
     if (maj_stat != GSS_S_COMPLETE) {
    display_status("unsealing message", maj_stat, min_stat);
    return(-1);
     } else if (! conf_state) {
    fprintf(stderr, "Warning!  Message not encrypted.\n");
     }

     (void) gss_release_buffer(&min_stat, &xmit_buf);

     fprintf(log, "Received message: ");
     cp = msg_buf.value;
     if ((isprint(cp[0]) || isspace(cp[0])) &&
     (isprint(cp[1]) || isspace(cp[1]))) {
    fprintf(log, "\"%.*s\"\n", msg_buf.length, msg_buf.value);
     } else {
    printf("\n");
    print_token(&msg_buf);
     }
      
     /* Produce a signature block for the message */
     maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
                &msg_buf, &xmit_buf);
     if (maj_stat != GSS_S_COMPLETE) {
    display_status("signing message", maj_stat, min_stat);
    return(-1);
     }

     (void) gss_release_buffer(&min_stat, &msg_buf);

     /* Send the signature block to the client */
     if (send_token(s, &xmit_buf) < 0)
    return(-1);

     (void) gss_release_buffer(&min_stat, &xmit_buf);

     /* Delete context */
     maj_stat = gss_delete_sec_context(&min_stat, &context, NULL);
     if (maj_stat != GSS_S_COMPLETE) {
    display_status("deleting context", maj_stat, min_stat);
    return(-1);
     }

     fflush(log);

     return(0);
}

Accepting a Context

Establishing a context typically involves a series of token exchanges between the client and the server. Both context acceptance and context initialization should be performed in loops to maintain program portability. The loop for accepting a context is very similar to the loop for establishing a context, although in reverse. Compare with Establishing a Security Context With the Server.

The following source code illustrates the server_establish_context() function.


Note - The source code for this example is also available through the Oracle download center. See http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html.


Example 6-4 server_establish_context() Function

/*
 * Function: server_establish_context
 *
 * Purpose: establishes a GSS-API context as a specified service with
 * an incoming client, and returns the context handle and associated
 * client name
 *
 * Arguments:
 *
 *      s               (r) an established TCP connection to the client
 *      service_creds   (r) server credentials, from gss_acquire_cred
 *      context         (w) the established GSS-API context
 *      client_name     (w) the client's ASCII name
 *
 * Returns: 0 on success, -1 on failure
 *
 * Effects:
 *
 * Any valid client request is accepted.  If a context is established,
 * its handle is returned in context and the client name is returned
 * in client_name and 0 is returned.  If unsuccessful, an error
 * message is displayed and -1 is returned.
 */
int server_establish_context(s, server_creds, context, client_name, ret_flags)
     int s;
     gss_cred_id_t server_creds;
     gss_ctx_id_t *context;
     gss_buffer_t client_name;
     OM_uint32 *ret_flags;
{
     gss_buffer_desc send_tok, recv_tok;
     gss_name_t client;
     gss_OID doid;
     OM_uint32 maj_stat, min_stat, acc_sec_min_stat;
     gss_buffer_desc    oid_name;

     *context = GSS_C_NO_CONTEXT;
     
     do {
          if (recv_token(s, &recv_tok) < 0)
               return -1;

          if (verbose && log) {
              fprintf(log, "Received token (size=%d): \n", recv_tok.length);
              print_token(&recv_tok);
          }

          maj_stat =
               gss_accept_sec_context(&acc_sec_min_stat,
                                      context,
                                      server_creds,
                                      &recv_tok,
                                      GSS_C_NO_CHANNEL_BINDINGS,
                                      &client,
                                      &doid,
                                      &send_tok,
                                      ret_flags,
                                      NULL,     /* ignore time_rec */
                                      NULL);    /* ignore del_cred_handle */

          (void) gss_release_buffer(&min_stat, &recv_tok);

          if (send_tok.length != 0) {
               if (verbose && log) {
                    fprintf(log,
                          "Sending accept_sec_context token (size=%d):\n",
                          send_tok.length);
                    print_token(&send_tok);
               }
               if (send_token(s, &send_tok) < 0) {
                    fprintf(log, "failure sending token\n");
                    return -1;
               }

               (void) gss_release_buffer(&min_stat, &send_tok);
          }
          if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
               display_status("accepting context", maj_stat,
                              acc_sec_min_stat);
               if (*context == GSS_C_NO_CONTEXT)
                       gss_delete_sec_context(&min_stat, context,
                                              GSS_C_NO_BUFFER);
               return -1;
          }

          if (verbose && log) {
              if (maj_stat == GSS_S_CONTINUE_NEEDED)
                  fprintf(log, "continue needed...\n");
              else
                  fprintf(log, "\n");
              fflush(log);
          }
     } while (maj_stat == GSS_S_CONTINUE_NEEDED);

     /* display the flags */
     display_ctx_flags(*ret_flags);

     if (verbose && log) {
         maj_stat = gss_oid_to_str(&min_stat, doid, &oid_name);
         if (maj_stat != GSS_S_COMPLETE) {
             display_status("converting oid->string", maj_stat, min_stat);
             return -1;
         }
         fprintf(log, "Accepted connection using mechanism OID %.*s.\n",
                 (int) oid_name.length, (char *) oid_name.value);
         (void) gss_release_buffer(&min_stat, &oid_name);
     }

     maj_stat = gss_display_name(&min_stat, client, client_name, &doid);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("displaying name", maj_stat, min_stat);
          return -1;
     }
     maj_stat = gss_release_name(&min_stat, &client);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("releasing name", maj_stat, min_stat);
          return -1;
     }
     return 0;
}

The sign_server() function uses the following source code to call server_establish_context() to accept the context.

/* Establish a context with the client */
     if (server_establish_context(s, server_creds, &context,
                  &client_name, &ret_flags) < 0)
    return(-1);

The server_establish_context() function first looks for a token that the client sends as part of the context initialization process. Because, GSS-API does not send or receive tokens itself, programs must have their own routines for performing these tasks. The server uses recv_token() for receiving the token:

     do {
          if (recv_token(s, &recv_tok) < 0)
               return -1;

Next, server_establish_context() calls the GSS-API function gss_accept_sec_context():

     maj_stat = gss_accept_sec_context(&min_stat,
                                      context,
                                      server_creds,
                                      &recv_tok,
                                      GSS_C_NO_CHANNEL_BINDINGS,
                                      &client,
                                      &doid,
                                      &send_tok,
                                      ret_flags,
                                      NULL,     /* ignore time_rec */
                                      NULL);    /* ignore del_cred_handle */

The acceptance loop continues, barring any errors, as long as gss_accept_sec_context() sets maj_stat to GSS_S_CONTINUE_NEEDED. If maj_stat is not equal to that value or to GSS_S_COMPLETE, a problem exists and the loop exits.

gss_accept_sec_context() returns a positive value for the length of send_tok whether a token exists to send back to the client. The next step is to see a token exists to be sent, and, if so, to send the token:

     if (send_tok.length != 0) {
          . . .
          if (send_token(s, &send_tok) < 0) {
               fprintf(log, "failure sending token\n");
               return -1;
          }

          (void) gss_release_buffer(&min_stat, &send_tok);
          }

Unwrapping the Message

After accepting the context, the sign_server() receives the message that has been sent by the client. Because the GSS-API does not provide a function for receiving tokens, the program uses the recv_token() function:

if (recv_token(s, &xmit_buf) < 0)
     return(-1);

Because the message might be encrypted, the program uses the GSS-API function gss_unwrap() for unwrapping:

maj_stat = gss_unwrap(&min_stat, context, &xmit_buf, &msg_buf,
                           &conf_state, (gss_qop_t *) NULL);
     if (maj_stat != GSS_S_COMPLETE) {
        display_status("unwrapping message", maj_stat, min_stat);
        return(-1);
     } else if (! conf_state) {
        fprintf(stderr, "Warning!  Message not encrypted.\n");
     }

     (void) gss_release_buffer(&min_stat, &xmit_buf);

gss_unwrap() takes the message that recv_token() has placed in xmit_buf, translates the message, and puts the result in msg_buf. Two arguments to gss_unwrap() are noteworthy. conf_state is a flag to indicate whether confidentiality, that is, encryption, has been applied to this message. The final NULL indicates that the program does not need to know that the QOP that was used to protect the message.

Signing and Returning the Message

At this point, the sign_server() function needs to sign the message. Signing a message entails returning the message's Message Integrity Code or MIC to the client. Returning the message proves that the message was sent and was unwrapped successfully. To obtain the MIC, sign_server() uses the function gss_get_mic():

maj_stat = gss_get_mic(&min_stat, context, GSS_C_QOP_DEFAULT,
                            &msg_buf, &xmit_buf);

gss_get_mic() looks at the message in msg_buf, produces the MIC, and stores the MIC in xmit_buf. The server then sends the MIC back to the client with send_token(). The client verifies the MIC with gss_verify_mic(). See Reading and Verifying a Signature Block From a GSS-API Client.

Finally, sign_server() performs some cleanup. sign_server() releases the GSS-API buffers msg_buf and xmit_buf with gss_release_buffer(). Then sign_server() destroys the context with gss_delete_sec_context().

Using the test_import_export_context() Function

GSS-API allows you to export and import contexts. These activities enable you to share a context between different processes in a multiprocess program. sign_server() contains a proof-of-concept function, test_import_export_context(), that illustrates how exporting and importing contexts works. test_import_export_context() does not pass a context between processes. Instead, test_import_export_context() displays the amount of time to export and then import a context. Although an artificial function, test_import_export_context() does indicate how to use the GSS-API importing and exporting functions. test_import_export_context() also shows how to use timestamps with regard to manipulating contexts.

The source code for test_import_export_context() is shown in the following example.


Note - The source code for this example is also available through the Oracle download center. See http://www.oracle.com/technetwork/indexes/downloads/sdlc-decommission-333274.html.


Example 6-5 test_import_export_context()

int test_import_export_context(context)
        gss_ctx_id_t *context;
{
        OM_uint32       min_stat, maj_stat;
        gss_buffer_desc context_token, copied_token;
        struct timeval tm1, tm2;

        /*
         * Attempt to save and then restore the context.
         */
        gettimeofday(&tm1, (struct timezone *)0);
        maj_stat = gss_export_sec_context(&min_stat, context, &context_token);
        if (maj_stat != GSS_S_COMPLETE) {
                display_status("exporting context", maj_stat, min_stat);
                return 1;
        }
        gettimeofday(&tm2, (struct timezone *)0);
        if (verbose && log)
                fprintf(log, "Exported context: %d bytes, %7.4f seconds\n",
                        context_token.length, timeval_subtract(&tm2, &tm1));
        copied_token.length = context_token.length;
        copied_token.value = malloc(context_token.length);
        if (copied_token.value == 0) {
            fprintf(log, "Couldn't allocate memory to copy context token.\n");
            return 1;
        }
        memcpy(copied_token.value, context_token.value, copied_token.length);
        maj_stat = gss_import_sec_context(&min_stat, &copied_token, context);
        if (maj_stat != GSS_S_COMPLETE) {
                display_status("importing context", maj_stat, min_stat);
                return 1;
        }
        free(copied_token.value);
        gettimeofday(&tm1, (struct timezone *)0);
        if (verbose && log)
                fprintf(log, "Importing context: %7.4f seconds\n",
                        timeval_subtract(&tm1, &tm2));
        (void) gss_release_buffer(&min_stat, &context_token);
        return 0;
}