Developer's Guide to Oracle Solaris Security

Establishing a Security Context With the Server

After the connection is made, call_server() uses the function client_establish_context() to create the security context, as follows:

if (client_establish_context(s, service-name, deleg-flag, oid, &context,
                                  &ret-flags) < 0) {
          (void) close(s);
          return -1;
     }

The client_establish_context() performs the following tasks:

Translating a Service Name into GSS-API Format

The first task that client_establish_context() performs is to translate the service name string to internal GSS-API format by using gss_import_name().


Example 5–3 client_establish_context() – Translate Service Name

     /*
     * Import the name into target_name.  Use send_tok to save
     * local variable space.
     */

     send_tok.value = service_name;
     send_tok.length = strlen(service_name) + 1;
     maj_stat = gss_import_name(&min_stat, &send_tok,
                        (gss_OID) GSS_C_NT_HOSTBASED_SERVICE, &target_name);
     if (maj_stat != GSS_S_COMPLETE) {
          display_status("parsing name", maj_stat, min_stat);
          return -1;
     }

gss_import_name() takes the name of the service, which is stored in an opaque GSS_API buffer send_tok, and converts the string to the GSS_API internal name target_name. send_tok is used to save space instead of declaring a new gss_buffer_desc. The third argument is a gss_OID type that indicates the send_tok name format. This example uses GSS_C_NT_HOSTBASED_SERVICE, which means a service of the format service@host. See Name Types for other possible values for this argument.

Establishing a Security Context for GSS-API

Once the service has been translated to GSS-API internal format, the context can be established. To maximize portability, establishing context should always be performed as a loop.

Before entering the loop, client_establish_context() initializes the context and the token_ptr parameter. There is a choice in the use of token_ptr. token_ptr can point either to send_tok, the token to be sent to the server, or to recv_tok, the token that is sent back by the server.

Inside the loop, two items are checked:

The following pseudocode describes the loop:

do
     gss_init_sec_context()
     if no context was created
         exit with error;
     if the status is neither "complete" nor "in process"
         release the service namespace and exit with error;
     if there is a token to send to the server, that is, the size is nonzero
         send the token;
         if sending the token  fails,
               release the token and service namespaces. Exit with error;
         release the namespace for the token that was just sent;
     if  the context is not completely set up
         receive a token from the server;
while the context is not complete

The loop starts with a call to gss_init_sec_context(), which takes the following arguments:


Note –

The client does not need to acquire credentials before initiating a context. On the client side, credential management is handled transparently by the GSS-API. That is, the GSS-API knows how to get credentials that are created by this mechanism for this principal. As a result, the application can pass gss_init_sec_context() a default credential. On the server side, however, a server application must explicitly acquire credentials for a service before accepting a context. See Acquiring Credentials.


After checking that a context or part of one exists and that gss_init_sec_context() is returning valid status, connect_to_server() checks that gss_init_sec_context() has provided a token to send to the server. If no token is present, the server has signalled that no other tokens are needed. If a token has been provided, then that token must be sent to the server. If sending the token fails, the namespaces for the token and service cannot be determined, and connect_to_server() exits. The following algorithm checks for the presence of a token by looking at the length:

if (send_tok_length != 0) {
     if (send_token(s, &send_tok) < 0) {
          (void) gss_release_buffer(&min_stat, &send_tok);
          (void) gss_release_name(&min_stat, &target_name);
          return -1;
     }
}

send_token() is not a GSS-API function and needs to be written by the user. The send_token() function writes a token to the file descriptor. send_token() returns 0 on success and –1 on failure. GSS-API does not send or receive tokens itself. The calling applications are responsible for sending and receiving any tokens that have been created by GSS-API.

The source code for the context establishment loop is provided below.


Note –

The source code for this example is also available through the Sun download center. See http://www.sun.com/download/products.xml?id=41912db5.



Example 5–4 Loop for Establishing Contexts

/*
 * Perform the context establishment loop.
 *
 * On each pass through the loop, token_ptr points to the token
 * to send to the server (or GSS_C_NO_BUFFER on the first pass).
 * Every generated token is stored in send_tok which is then
 * transmitted to the server; every received token is stored in
 * recv_tok, which token_ptr is then set to, to be processed by
 * the next call to gss_init_sec_context.
 *
 * GSS-API guarantees that send_tok's length will be non-zero
 * if and only if the server is expecting another token from us,
 * and that gss_init_sec_context returns GSS_S_CONTINUE_NEEDED if
 * and only if the server has another token to send us.
 */

token_ptr = GSS_C_NO_BUFFER;
*gss_context = GSS_C_NO_CONTEXT;
1234567890123456789012345678901234567890123456789012345678901234567890123456

do {
    maj_stat =
        gss_init_sec_context(&min_stat, GSS_C_NO_CREDENTIAL, 
        gss_context, target_name, oid, 
        GSS_C_MUTUAL_FLAG | GSS_C_REPLAY_FLAG | deleg_flag,
        0, NULL,                      /* no channel bindings */
        token_ptr, NULL,              /* ignore mech type */
        &send_tok, ret_flags, NULL);  /* ignore time_rec */
    if (gss_context == NULL){
        printf("Cannot create context\n");
        return GSS_S_NO_CONTEXT;
    }
    if (token_ptr != GSS_C_NO_BUFFER)
        (void) gss_release_buffer(&min_stat, &recv_tok);
    if (maj_stat!=GSS_S_COMPLETE && maj_stat!=GSS_S_CONTINUE_NEEDED) {
        display_status("initializing context", maj_stat, min_stat);
        (void) gss_release_name(&min_stat, &target_name);
        return -1;
    }

    if (send_tok.length != 0){
        fprintf(stdout, "Sending init_sec_context token (size=%ld)...",
            send_tok.length);
        if (send_token(s, &send_tok) < 0) {
            (void) gss_release_buffer(&min_stat, &send_tok);
            (void) gss_release_name(&min_stat, &target_name);
            return -1;
        }
    }
    (void) gss_release_buffer(&min_stat, &send_tok);

    if (maj_stat == GSS_S_CONTINUE_NEEDED) {
        fprintf(stdout, "continue needed...");
        if (recv_token(s, &recv_tok) < 0) {
            (void) gss_release_name(&min_stat, &target_name);
            return -1;
        }
        token_ptr = &recv_tok;
    }
    printf("\n");
} while (maj_stat == GSS_S_CONTINUE_NEEDED);

For more information on how send_token() and recv_token() work, see Miscellaneous GSS-API Sample Functions.