SSPI/Kerberos Interoperability with GSSAPI

Care must be taken when using the Kerberos security support provider (SSP) if interoperability with GSSAPI is a requirement. The following code conventions allow interoperability with GSSAPI-based applications:

You can find sample code in the Platform Software Development Kit (SDK) under Samples\Security\SSPI\GSS. In addition, the equivalent UNIX sample is distributed in the MIT and Heimdal Kerberos distributions, GSS client and server.

Windows-Compatible Names

GSSAPI functions use a name format known as gss_nt_service_name as specified in the RFC. For example, sample@host.dom.com is a name that can be used in a GSSAPI-based application. The Windows operating system does not recognize the gss_nt_service_name format, and the full service principal name, for example sample/host.dom.com@REALM, must be used.

Authentication

Authentication is usually handled when a connection is first set up between a client and a server. In this sample, the client is using Security Support Provider Interface (SSPI) and the server is using GSSAPI.

To set up authentication in the SSPI client

  1. Get outbound credentials by using AcquireCredentialsHandle.
  2. Create a service name with gss_import_name() and get inbound credentials by using gss_acquire_cred.
  3. Get an authentication token to send to the server by using InitializeSecurityContext (Kerberos).
  4. Send the token to the server.

To set up authentication in the GSSAPI server

  1. Parse the message from the client to extract the security token. Use the gss_accept_sec_context function, passing the token as an argument.

  2. Parse the message from the server to extract the security token. Pass this security token to InitializeSecurityContext (Kerberos).

  3. Send a response token to the client.

    The gss_accept_sec_context function can return a token that you can send back to the client.

  4. If it is necessary to continue, send a response token to the server; otherwise, the setup of authentication is complete.

  5. If it is necessary to continue, wait for the next token from the client; otherwise, the setup of authentication is complete.

Message Integrity and Privacy

Most GSSAPI-based applications use the GSS_Wrap function to sign a message before sending it. Conversely, the GSS_Unwrap function verifies the signature. GSS_Wrap is available in version 2.0 of the API and is now widely used and specified in Internet standards that describe using the GSSAPI for adding security to protocols. Earlier, the GSS SignMessage and SealMessage functions were used for message integrity and privacy. GSS_Wrap and GSS_Unwrap are used for both integrity and privacy with the use of privacy controlled by the value of the "conf_flag" argument.

If a GSSAPI-based protocol is specified to use the gss_get_mic and gss_verify_mic functions, the correct SSPI functions would be MakeSignature and VerifySignature. Be aware that MakeSignature and VerifySignature will not interoperate with GSS_Wrap when conf_flag is set to zero, or with GSS_Unwrap. The same is true for mixing EncryptMessage (Kerberos) set for signature only and gss_verify_mic.

Note

Do not use the MakeSignature or VerifySignature functions when GSS_Wrap and GSS_Unwrap are called for.

 

The SSPI equivalent to GSS_Wrap is EncryptMessage (Kerberos) for both integrity and privacy.

The following example shows using EncryptMessage (Kerberos) to sign data that will be verified by GSS_Unwrap.

In the SSPI client:

// Need three descriptors, two for the SSP and
// one to hold the application data. 
in_buf_desc.cBuffers = 3;
in_buf_desc.pBuffers = wrap_bufs;
in_buf_desc.ulVersion = SECBUFFER_VERSION;
wrap_bufs[0].cbBuffer = sizes.cbSecurityTrailer;
wrap_bufs[0].BufferType = SECBUFFER_TOKEN;
wrap_bufs[0].pvBuffer = malloc(sizes.cbSecurityTrailer);

// This buffer holds the application data.
wrap_bufs[1].BufferType = SECBUFFER_DATA;
wrap_bufs[1].cbBuffer = in_buf.cbBuffer;
wrap_bufs[1].pvBuffer = malloc(wrap_bufs[1].cbBuffer);
memcpy(wrap_bufs[1].pvBuffer, in_buf.pvBuffer, in_buf.cbBuffer);
wrap_bufs[2].BufferType = SECBUFFER_PADDING;
wrap_bufs[2].cbBuffer = sizes.cbBlockSize;
wrap_bufs[2].pvBuffer = malloc(wrap_bufs[2].cbBuffer);
maj_stat = EncryptMessage(&context,
SignOnly ? KERB_WRAP_NO_ENCRYPT : 0,
&in_buf_desc, 0);

// Send a message to the server.

In the GSSAPI server:

// Received message is in recv_buf. 
maj_stat = gss_unwrap(&min_stat, context, &recv_buf, &msg_buf,
    &conf_state, (gss_qop_t *) NULL);
(void) gss_release_buffer(&min_stat, &recv_buf);

// Original message is in msg_buf.

The SSPI equivalent to GSS_Unwrap is DecryptMessage (Kerberos). Here is an example that shows how to use DecryptMessage (Kerberos) to decrypt data that was encrypted by GSS_Wrap.

In the GSSAPI server:

// Seal the message.
send_buf.value = msg;
send_buf.length = msglen;

// If encrypt_flag = 1, privacy; encrypt_flag = 0, integrity.
maj_stat = gss_wrap(&min_stat, context, encrypt_flag,
    GSS_C_QOP_DEFAULT, &send_buf, &state, &msg_buf); 

// The message to send is in msg_buf.

In the SSPI client:

wrap_buf_desc.cBuffers = 2;
wrap_buf_desc.pBuffers = wrap_bufs;
wrap_buf_desc.ulVersion = SECBUFFER_VERSION; 

// This buffer is for SSPI.
wrap_bufs[0].BufferType = SECBUFFER_STREAM;
wrap_bufs[0].pvBuffer = xmit_buf.pvBuffer;
wrap_bufs[0].cbBuffer = xmit_buf.cbBuffer;

// This buffer holds the application data.
wrap_bufs[1].BufferType = SECBUFFER_DATA;
wrap_bufs[1].cbBuffer = 0;
wrap_bufs[1].pvBuffer = NULL;
maj_stat = DecryptMessage(
&context,
&wrap_buf_desc,
0, // no sequence number
&qop
);

// This is where the data is.
msg_buf = wrap_bufs[1];

// Check QOP of received message.
// If QOP is KERB_WRAP_NO_ENCRYPT, the message is signed only; 
// otherwise, it is encrypted.