Client Initialization

The example in this topic shows the initialization code to add to the client of a transport application to connect using NTLMSSP authentication.

The initialization when the client starts is similar to that described for the server in Server Initialization. The client calls the InitSecurityInterface function. As with the server, if the client is binding directly to SECURITY.DLL, it can discard the returned function table; otherwise, it must use the function table to make subsequent calls to the security provider functions. The client must also call the QuerySecurityPackageInfo function to get the maximum security buffer size.

When the client is ready to make a connection, it calls the AcquireCredentialsHandle function. The client must save the client credential handle for use when calling the InitializeSecurityContext function, but it can discard the other parameters after the call to AcquireCredentialsHandle.

If the client program needs to use additional credentials instead of the logon credentials (the user name, domain name, and password the user logged on with), it must provide them in the AcquireCredentialsHandle call. Note that the SEC_WINNT_AUTH_IDENTITY structure used to specify additional credentials in the following example is a package-specific structure defined in RPCDCE.H.

SEC_WINNT_AUTH_IDENTITY AdditionalCredentials;
BOOL SupplyCredentials;
CredHand ClientCredentials;
TimeStamp Expiration;

//
// If there are additional credentials stored in UserName, 
// DomainName, and Password, fill them in here
//

#ifdef UNICODE
AdditionalCredentials.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE
#else
AdditionalCredentials.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI
#endif

//
// Set the flag indicating that we are supplying credentials to 
// FALSE. We will set this to TRUE if a user name, domain name, or 
// password has been supplied. If this is FALSE when we call
// AcquireCredentialsHandle, we will pass in NULL for the pAuthData 
// parameter.
//

SupplyCredentials = FALSE;

if (UserName != NULL) 
{
    AdditionalCredentials.User = UserName;
    AdditionalCredentials.UserLength = strlen(UserName);
    //
    // Set the flag to indicate that we are supplying credentials.
    //
    SupplyCredentials = TRUE;
}

//
// Do the same thing for the password and domain name.
//

SecStatus = AcquireCredentialsHandle(
    NULL,                       // no principal name
    TEXT("NTLM"),               // package name
    SECPKG_CRED_OUTBOUND,       // credential use flag
    NULL,                       // no logon identifier
    SupplyCredentials ?         // If we are supplying credentials,
      &AdditionalCredentials :  // pass in the additional credentials.
      NULL,                     
    NULL,                       // no GetKey function
    NULL,                       // no GetKey function argument
    &ClientCredential,          // receives the new credential
    &Expiration                 // receives the expiration time of 
                                // the credential
    );
 

The credential handle does not expire, so the client can ignore the expiration time for this security package.

Next, the client calls the InitializeSecurityContext function to start setting up the security context.

CtxtHandle ClientContext;
TimeStamp Expiration;
SecBufferDesc OutputBufferDescriptor;
SecBuffer OutputSecurityToken;
ULONG ContextRequirements;
ULONG ContextAttributes;

//
// Build the output buffer descriptor.
//

OutputBufferDescriptor.cBuffers = 1
OutputBufferDescriptor.pBuffers = &OutputSecurityToken;
OutputBufferDescriptor.ulVersion = SECBUFFER_VERSION;

OutputSecurityToken.BufferType = SECBUFFER_TOKEN;
OutputSecurityToken.cbBuffer = PackageInfo->cbMaxToken;
OutputSecurityToken.pvBuffer = 
    LocalAlloc(0, OutputSecurityToken.cbBuffer);

//
// Check for memory allocation failure here.
//

//
// Compute context requirements.
// If you want message integrity, ask for 
//     replay or sequence detection.
// If you want message encryption, ask for confidentiality.
//

ContextRequirements = ISC_REQ_REPLAY_DETECT;

//
// Assume that TargetName is the name of the target server, or NULL.
// Make the call to InitializeSecurityContext.
//

SecStatus = InitializeSecurityContext(
      &ClientCredential
      NULL,                     // no context handle yet
      TargetName,               // target name, if available
      ContextRequirements,
      0,                        // reserved parameter
      SECURITY_NATIVE_DREP,     // target data representation
      NULL,                     // no input buffer
      0,                        // reserved parameter
      &ClientContext,           // receives new context handle
      &ContextAttributes,       // receives context attributes
      &OutputBufferDescriptor,  // receives output security token
      &Expiration               // receives context expiration time
      );
 

This InitializeSecurityContext call returns SEC_I_CONTINUE_NEEDED on success, or an error code on failure. If the function is successful, the client transmits the token buffer to the server. The token buffer is stored in the pvBuffer member of the OutputSecurityToken structure. The cbBuffer member of the structure specifies the length of the buffer.

To finish creating the security token, the server must call the AcceptSecurityContext function and send the output back to the client. The client calls InitializeSecurityContext again, followed by another call by the server to AcceptSecurityContext. Server Authentication describes this sequence of calls.