Go to main content

Developer's Guide to Oracle® Solaris 11.3 Security

Exit Print View

Updated: April 2020
 
 

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;
     }
  • s is a file descriptor that represents the connection that is established by connect_to_server().

  • service-name is the requested network service.

  • deleg-flag specifies whether the server can act as a proxy for the client.

  • oid is the mechanism.

  • context is the context to be created.

  • ret-flags is an int that specifies any flags to be returned by the GSS-API function gss_init_sec_context().

    The client_establish_context() performs the following tasks:

  • Translates the service name into internal GSSAPI format

  • Performs a loop of token exchanges between the client and the server until the security context is complete

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 15  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 status that is returned by gss_init_sec_context()

    The return status catches any errors that might require the loop to be aborted. gss_init_sec_context() returns GSS_S_CONTINUE_NEEDED if and only if the server has another token to send.

  • The size of token to be sent to the server, which is generated by gss_init_sec_context().

    A token size of zero indicates that no more information exists that can be sent to the server and that the loop can be exited. The token size is determined from token_ptr.

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:

  • The status code to be set by the underlying mechanism.

  • The credential handle. The example uses GSS_C_NO_CREDENTIAL to act as a default principal.

  • gss-context, which represents the context handle to be created.

  • target-name of the service, as a GSS_API internal name.

  • oid, the ID for the mechanism.

  • Request flags. In this case, the client requests that the server authenticate itself, that message-duplication be turned on, and that the server act as a proxy if requested.

  • No time limit for the context.

  • No request for channel bindings.

  • token_ptr, which points to the token to be received from the server.

  • The mechanism actually used by the server. The mechanism is set to NULL here because the application does not use this value.

  • &send_tok, which is the token that gss_init_sec_context() creates to send to the server.

  • Return flags. Set to NULL because they are ignored in this example.


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.

Example 16  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 about how send_token() and recv_token() work, see Miscellaneous GSS-API Sample Functions.