GSS-API Programming Guide

Accepting a Context, Getting and Signing Data

Having acquired credentials for the service, the server program checks to see if the user has specified using inetd (see Overview: main() (Server) and Overview: main() (Server)) and then calls sign_server(), which does the main work of the program. The first thing that sign_server() does is establish the context by calling server_establish_context().


Note –

inetd is not covered here. Basically, if inetd has been specified, the program calls sign_server() on the standard input. If not, it creates a socket, accepts a connection, and then calls sign_server() on that connection.


sign_server() does the following:

  1. Accepts the context.

  2. Unwraps the data.

  3. Signs the data.

  4. Returns the data.

Accepting a Context

Because establishing a context can involve 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. Indeed, the loop for accepting a context is very similar to that for establishing one, although rather in reverse. (Compare with Establishing a Context.)

  1. The first thing the server does is look for a token that the client should have sent as part of the context initialization process. Remember, the GSS-API does not send or receive tokens itself, so programs must have their own routines for performing these tasks. The one the server uses for receiving the token is called recv_token() (it can be found at recv_token()):


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

  2. Next, the program 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 */

    where
    • min_stat is the error status returned by the underlying mechanism.

    • context is the context being established.

    • server_creds is the credential for the service being provided (see Acquiring Credentials).

    • recv_tok is the token received from the client by recv_token().

    • GSS_C_NO_CHANNEL_BINDINGS is a flag indicating not to use channel bindings (see Channel Bindings).

    • client is the ASCII name of the client.

    • oid is the mechanism (in OID format).

    • send_tok is the token to send to the client.

    • ret_flags are various flags indicating whether the context supports a given option, such as message-sequence-detection.

    • NULL and NULL indicate that the program is not interested in the length of time the context will be valid, nor in whether the server can act as a client's proxy.

    The acceptance loop continues (barring an error) as long as gss_accept_sec_context() sets maj_stat to GSS_S_CONTINUE_NEEDED. If maj_stat is not equal to either that value nor to GSS_S_COMPLETE, there's a problem and the loop exits.

  3. gss_accept_sec_context() returns a positive value for the length of send_tok if there is a token to send back to the client. The next step is to see if there's a token to send, and, if so, to send it:


         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 server receives the message sent by the client. Because the GSS-API doesn't provide a function to do this, the program uses its own function, recv_token():


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

Since the message might be encrypted, the program uses the GSS-API function gss_unwrap() to unwrap it:


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 it, and puts the result in msg_buf. Two arguments to gss_unwrap() are noteworthy: conf_state is a flag to indicate whether confidentiality was applied for this message (that is, if the data is encrypted or not), and the final NULL indicates that the program isn't interested in the QOP used to protect the message.

Signing the Message, Sending It Back

All that is left, then, is for the server to “sign” the message — that is, to return the message's MIC (Message Integrity Code, a unique tag associated with message) to the client to prove that the message was sent and unwrapped successfully. To do that, the program uses the function gss_get_mic():


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

which looks at the message in msg_buf and produces the MIC from it, storing it in xmit_buf. The server then sends the MIC back to the client with send_token(), and the client verifies it with gss_verify_mic(). See Verifying the Message.

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

Importing and Exporting a Context

As noted in Context Export and Import, the GSS-API allows you to export and import contexts. The usual reason for doing this is to share a context between different processes in a multiprocess program.

sign_server() contains a proof-of-concept function, test_import_export_context(), which illustrates how exporting and importing contexts works. This function doesn't pass a context between processes. It only displays the amount of time it takes to export and then import a context. Although rather an artificial function, it does indicate how to use the GSS-API importing and exporting functions, as well as give an idea of how to use timestamps with regard to manipulating contexts. test_import_export_context() can be found in test_import_export_context().