SSPI/Kerberos Interoperability with GSSAPI

 

John Brezak
Microsoft Corporation

December 1999

Summary: An overview of coding conventions that should be used for interoperability between SSPI-based Kerberos operations and GSSAPI-based Kerberos operations.

Many applications that use Kerberos for authentication have been coded to use GSSAPI. In fact, many standard protocols are often specified referencing GSSAPI as the mechanism that is used for authentication. The Generic Security Services API (GSSAPI) is described in IETF RFC-2078, available at www.ietf.org/rfc/rfc2078.txt. Because Microsoft Windows 2000 implements the Security Service Provider Interface (SSPI) and not the GSSAPI, care must be taken when using the Kerberos Security Service Provider (SSP) when interoperability with GSSAPI is a requirement. This is a quick overview of coding conventions that should be used to interoperate with GSSAPI-based applications.

Windows 2000 uses the SSPI extensively. In fact, the SSPI has been used in Microsoft products for many years and there are SSPs for other authentication protocols such as NTLM and SSL, to name a few. SSPI and GSSAPI are very similar in terms of semantics. They evolved in parallel and each was driven by different goals. As you read this article and examine the samples, you will see how similar they really are.

Names

The GSSAPI uses a name format known as gss_nt_service_name (nt is not Windows NT), as specified in the RFC-2078. This would look like sample@host.dom.com when used in a GSSAPI-based application. In Windows 2000, the gss_nt_service_name format is not recognized and the full service principal name should be used. For example, the gss_nt_service_name of sample@host.dom.com in realm RESKIT.COM would be sample/host.dom.com@RESKIT.COM.

Authentication

Authentication is usually done when a connection is first set up between a client and server. In this example, the client is using SSPI and the server is using GSSAPI.

Client (SSPI) Communications Server (GSSAPI)
Get outbound credentials using AcquireCredentialsHandle()   Create service name with gss_import_name() and get inbound credentials using gss_aquire_cred().
Get authentication token to send to server using InitializeSecurityContext() Send token to Server. Parse message from client to extract security token. Use gss_accept_sec_context() passing token as argument.
Parse message from server to extract security token. Pass to InitializeSecurityContext() Send response token to client. gss_accept_sec_context() can return a token to send back to the client.
If continue needed, send token to server; otherwise, connection is completed.   If continue needed, wait for next token from client; otherwise, connection is completed.
  Exchange application data.  

Message Integrity and Privacy

Most GSSAPI-based applications will use GSS_Wrap to sign a message before sending it. Conversely, the GSS_Unwrap will be used to verify the signature. GSS_Wrap is a new part of the API (v2) and is now widely used and specified in Internet standards that describe using the GSSAPI for adding security to protocols. Before these APIs existed, 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. The use of privacy is controlled by the conf_flag argument.

If a GSSAPI-based protocol is specified to use gss_get_mic and gss_verify_mic, then the correct SSPI functions would be MakeSignature and VerifySignature. Be aware that MakeSignature and VerifySignature will not interoperate with gss_wrap(conf_flag=0) and gss_unwrap(). The same is true for mixing EncryptMessage(signonly) and gss_verify_mic.

Note

   

Do not use MakeSignature or VerifySignature functions when GSS_Wrap or GSS_Unwrap is called for.

The SSPI equivalent to GSS_Wrap is EncryptMessage for both integrity and privacy. Here is a code sample that shows how to use EncryptMessage to sign data that will be verified by GSS_Unwrap:

Client (SSPI)   Server (GSSAPI)
// Need 3 descriptors - 2 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 buf 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 message to 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. Here is a code sample that shows how to use DecryptMessage to decrypt data that was encrypted by a GSS_Wrap:

Client (SSPI) Server (GSSAPI)
wrap_buf_desc.cBuffers = 2;
wrap_buf_desc.pBuffers =
              wrap_bufs;
wrap_buf_desc.ulVersion = 
              SECBUFFER_VERSION; 
// This buf is for SSPI
wrap_bufs[0].BufferType = 
              SECBUFFER_STREAM;
wrap_bufs[0].pvBuffer = 
              xmit_buf.pvBuffer;
wrap_bufs[0].cbBuffer =
              xmit_buf.cbBuffer;
// This buf 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
printf(“Received message %s: “,
   (qop == KERB_WRAP_NO_ENCRYPT ? 
   “signed only” : “encrypted”)
   );
// 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

Samples

As part of the MIT Kerberos (further information available at https://web.mit.edu/kerberos/www/index.html) and Heimdal Kerberos (further information available at www.pdc.kth.se/heimdal/) implementations, there is a GSSAPI sample client and server. A modified sample that uses SSPI is included in the Windows 2000 SDK under samples\WinBase\security\win2000\GSS.