Kerberizing Applications Using Security Support Provider Interface
- SSPI and Windows 2000 Security Architecture
- Sample Project: Using SSPI to Kerberize Applications
- Summary
- References
In this chapter, we examine the Security Support Provider Interface (SSPI), the common interface to request security services in Windows 2000, and show how to use SSPI to kerberize applications. We also define some important security concepts and explain how Windows 2000 provides security services to applications.
SSPI and Windows 2000 Security Architecture
The Security Support Provider Interface (SSPI) is an application programming interface (API) that applications should use to request security services from security service providers. In Windows 2000, a security support provider-also called a security provider-is a dynamic link library that supports the SSPI specification and provides one or more security support packages. Each security support package (SSP)-also called security package-supports one specific security protocol and implements the SSPI functions according to that security protocol. Kerberos, NTLM, and SSL are examples of security packages that are provided by the default security provider in Windows 2000.
SSPI defines the following interface categories:
-
Credential management interfaces to access credentials, such as passwords, tickets, or to free accessed credentials.
-
Context management interfaces to create and to use security contexts. Security contexts, created by both sides of a communication link, provide the information needed by message-support interfaces to carry out secure communication.
-
Message support interfaces to provide communication integrity and privacy.
-
Package management interfaces to get information about the packages supported by the security provider.
In a typical SSPI scenario, applications first load a security provider DLL and use the package management functions to find information about the security package they want to use. Then, the applications initialize the desired security context through the context management functions and, finally, use the message support functions to sign and to encrypt messages or to verify signatures and to decrypt encrypted messages. By providing these common interfaces, SSPI hides the details of how to use a specific package from the application and also allows an application to use services provided by any security package without changing the interface to use such services.
It is important to know that SSPI does not provide any interfaces for sending and receiving messages. Distributed applications should use other interfaces, such as WinSock, to transfer messages constructed by the SSPI functions.
Figure 11-1 shows how SSPI fits into the overall security architecture of Windows 2000. SSPI is the abstraction layer between applications and security protocols supported by Windows 2000. Applications can use the services of a security package in various ways: either directly or through higher-level protocols or APIs, such as RPC, DCOM, WinSock, or WinInet. Nevertheless, one way or another, access to the security services is always through the SSPI layer.
SSPI Functions
Table 11-1 shows the SSPI functions supported by the credential management, context management, message support, and package management interface categories, respectively. The InitSecurityInterface function returns a pointer to a function table structure that contains pointers to all the SSPI functions. After loading a security
Figure 11-1 SSPI and the overall Windows 2000 distributed security architecture provider DLL, SSPI clients bind to the implementation of the SSPI functions at runtime by calling the InitSecurityInterface function. All the SSPI functions are called by using this function table. If a function is not implemented, its function pointer is set to NULL in the function table. SSPI clients should make sure that a function is implemented by comparing the function pointer to NULLbefore calling the function.
Table 11-1 SSPI Functions
Function |
Description |
Credential Management |
|
AcquireCredentialsHandle |
Acquires a handle to the credentials of the specified principal. Refer to Chapter 1 for the definition of a security principal. |
FreeCredentialsHandle |
Releases a credential handle and associated resources. |
QueryCredentialAttributes |
Queries credential attributes. |
Context Management |
|
InitializeSecurityContext |
Initiates a security context by generating a security token that must be passed to the server. The application that uses this function is called an SSPI client. |
AcceptSecurityContext |
Initiates a security context using the security token received from the client. The application that uses this function is called an SSPI server. |
DeleteSecurityContext |
Frees a security context and associated resources. |
QueryContextAttributes |
Queries security context attributes for information needed when signing or encrypting messages, and so on. |
QuerySecurityContextToken |
Obtains Windows 2000 access token associated with a client security context for direct use. |
ApplyControlToken |
Applies a control token to an existing security context. A control token can be used, for example, to gracefully shut down a secure connection in SSL. SSPI applications using Kerberos would not need this function. |
CompleteAuthToken |
Completes an authentication token. Used in some protocols, such as DCE RPC, in which the security context needs to be revised after the application has updated some message fields. Not used in Kerberos. |
FreeContextBuffer |
Frees a memory buffer allocated by the security provider. |
ImpersonateSecurityContext |
Impersonates the client's security context. |
RevertSecurityContext |
Stops impersonation. |
ExportSecurityContext |
Exports a security context into a buffer for later use. |
ImportSecurityContext |
Imports an exported security context into the current process. |
Message Support |
|
DecryptMessage |
Decrypts a message created by a call to EncryptMessage. |
EncryptMessage |
Encrypts a message. |
MakeSignature |
Signs a message. |
VerifySignature |
Verifies a signature created by a call to MakeSignature. |
Package Management |
|
InitSecurityInterface |
Returns a pointer to the SSPI function table. |
EnumerateSecurityPackages |
Lists all available security packages and their attributes. |
QuerySecurityPackageInfo |
Queries an individual security package for its attributes. |
Using SSPI
To use SSPI, distributed applications go through the following three major steps:
-
Initialize the SSPI.
-
Authenticate the connection.
-
Exchange secure messages over the authenticated connection, using security services, such as integrity, privacy, and so on.
The rest of this section gives an overview of what happens in each of these three steps on each side of the communication link. More detailed information comes later, when we go through our SSPI sample project on how to kerberize a distributed application.
Initializing the SSPI
Table 11-2 summarizes what needs to be done to initialize a security support package. Both the client and the server go through the initialization step in the same way and should choose the same package in order to be able to establish an authenticated connection and exchange messages.
Windows 2000 supports a security negotiation package that automatically selects a security package acceptable to both ends of communication. The negotiate package attempts to use the Kerberos package first and, if security negotiation fails, tries the NTLIM package next.
Authenticating the Connection
Establishing an authenticated connection is more complicated and differs for the client and the server. Here, the term client means SSPI client, which is that side of the communication that starts the process of building the security context by calling the InitializeSecurityContext function. The term server means SSPI server, which is the side that waits to receive a security token from the SSPI client and calls the AcceptSecurityContext function. An SSPI client could be the server part of a distributed application. Tables 11-3 and 11-4 summarize the steps to initialize a security support package on the client and server side, respectively.
When AcquireCredentialsHandle is called, the name of the chosen security package is passed to obtain the correct type of credential. Each security package requires a different type of credential, and the specifics of this operation are abstracted by SSPI.
Table 11-2 Initializing the SSPI for Client and Server
Step |
Description |
Load the security support provider. |
Use the LoadLibraryfunction to load the DLL that implements the package. Load secur32.dllfor the default security provider. |
Get a pointer to the package function table. |
Use the GetProcAddressfunction to obtain the address of the InitSecurityInterfacefunction. Get the security provider's function table by calling the InitSecurityInterfacefunction. |
Find the desired security package |
Call the EnumerateSecurityPackagesfunction to get information about security packages and choose one. |
Table 11-3 Authentication Steps for the Client
Step |
Description |
Get a handle to the credentials to use for authentication. |
Use the AcquireCredentialsHandlefunction to get a handle to the client's credentials. |
Authenticate and build the security context. |
Call the InitializeSecurityContextfunction as many times as needed to authenticate the connection and to build the security context. After each call, send any output from this function to the server. If you need to call this function again, wait to receive information from the server, and pass the received information to this function. The return value of this function tells you whether you need to call this function again. |
Check the security context. |
Make sure that the final attributes of the security context, returned in the final call to InitializeSecurityContext, are sufficient. If not, you may want to deny the connection. |
In their calls to InitializeSecurityContext and AcceptSecurityContext, the client and the server, respectively, can specify the type of authentication and security environment needed. At the end of the authentication step, each side should check and make sure that the established environment has the required security attributes and close the communication channel if that is not the case. Every call to these functions may have some output data that must be sent to the other. The return values of these functions specify whether they need to be called again. If so, the client or the server has to wait to receive security data from the other side and pass it to the function in the next call.
When Kerberos is used, a three-leg authentication is carried out, regardless of the client's choice for mutual authentication. This means that there will be two calls to the
InitializeSecurityContext function and one call to the AcceptSecurityContext function. In the first call to InitializeSecurityContext, the client communicates with the Key Distribution Center (KDC) to obtain a session ticket for the specified server, as specified by the Ticket Granting Service (TGS) Exchange protocol, if such a ticket is not already in the ticket cache. When it receives the session ticket-the output from the InitializeSecurityContext function-the server validates the ticket by calling the AcceptSecurityContext function, as specified by the Client/Server (CS) Exchange protocol. The output of the AcceptSecurityContext function needs to be sent to the client so that it can complete the construction of its security token. If mutual authentication is requested, this output also contains the authenticator message-the KRB_AP_REP message-that the client validates in its second call to the Initial-izeSecurityContext function.
Table 11-4 Authentication Steps for the Server
Step |
Description |
Get a handle to the credentials to use for authentication. |
Use the AcquireCredentialsHandlefunction to get a handle to the server's credentials. |
Authenticate and build the security context |
Call the AcceptSecurityContextfunction as many times as needed to authenticate the connection and to build the security context. Pass the information received from the client to this function every time you call this function. After each call, send the output from this function to the client and wait to receive information from the client if there is a need. The return value of this function tells you whether you need to call this function again. |
Check the security context. |
Make sure that the final attributes of the security context, returned in the final call to AcceptSecurityContext, are sufficient. If not, you may want to deny the connection. |
As mentioned earlier, it is up to the applications to exchange the data returned by the SSPI functions. Applications should define an application-level protocol and message formats for such exchanges.
Exchanging Messages
Having established an authenticated connection, the client and the server can proceed with exchanging secure messages by signing or encrypting messages, as shown in Table 11-5. To be able to use message signing and encryption, appropriate flags should be passed to the InitializeSecurityContext or the AcceptSecurityContext functions so that the appropriate security context is created during the authentication step. Both the client and the server can request such services and could end the connection if the requested services are denied.
Security context attributes specify the capabilities of the security context. Both sides of the communication can ask for security attributes when using the InitializeSecurityContext and the AcceptSecurityContext functions, by combining the values specified in Table 11-6. When using attributes, be aware that
-
Not all security attributes are available in a security package.
-
Some attributes, such as IDENTIFY, can be requested only by a specific side of the connection.
-
Some attributes, such as CONFIDENTIALITY, can be requested by either side of the communication.
-
Some attributes, such as CONNECTION, are honored only when requested by both sides of the connection.
The most important security attributes supported in Kerberos are listed in Table 11-6. For a complete list of context attributes, refer to [MICR00].
To make sure that the established context attributes are acceptable, both the client and the server should check the arguments in the InitializeSecurity Context and the AcceptSecurityContext functions, which specify the established attributes. If no context attributes are requested, the security package will use its default context attributes. In Kerberos, for example, the default security context allows only client authentication and impersonation. To be able to do mutual authentication, message signing, or encryption, the related context attribute should be requested.
The preceding security attributes should be prefixed with ISC_REQ_ and ASC_REQ_ before being used in the InitializeSecurityContext and the AcceptSecurityContext functions, respectively. For example, to request mutual authentication, the client should specify ISC_REQ_MUTUAL_AUTH with the InitializeSecurityContext function. The server can request confidentiality by using ASC_REQ_CONFIDENTIALITY with the AcceptSecurityContext function. The established context attributes are prefixed by ISC_RET_and ASC_RET.
Table 11-5 Message Exchange Steps for Client and Server
Step |
Description |
Sign a message before sending it. |
Use the MakeSignaturefunction to sign. |
Verify a signature on a signed message before accepting it. |
Use the VerifySignaturefunction to verify the signature. |
Encrypt a message before sending it. |
Use the EncryptMessagefunction to encrypt a message. |
Decrypt an encrypted message. |
Use the DecryptMessagefunction to decrypt an encrypted message. |
Table 11-6 Security Context Attributes in Kerberos
Attribute |
Description |
IDENTIFY |
The server can identify but cannot impersonate or delegate the client. This attribute is not supported by Kerberos and is available only in the SSPI specification. This attribute can be set only by the client side. |
DELEGATE |
The server can build a new security context impersonating the client, and that context will be accepted by other remote servers as the client's context. When neither DELEGATEnor IDENTIFYis used, impersonation is assumed by default. This attribute can be set only by the client side. |
MUTUAL_AUTH |
The communicating parties must authenticate their identities to each other. In Kerberos, without MUTUAL_AUTH, the client authenticates to the server; with MUTUAL_AUTH, the server must authenticate to the client. This flag can be set only by the client. |
REPLAY_DETECT |
Security package checks for replayed packets and notifies the caller if a packet has been replayed. The use of this flag implies all the conditions specified by the INTEGRITYflag. |
SEQUENCE_DETECT |
Out-of-order packets could be detected through the message support functions. Use of this flag implies all the conditions specified by the INTEGRITYflag. |
CONFIDENTIALITY |
Data can be encrypted using the message support functions. Use this flag if you need to encrypt and to decrypt the data. |
INTEGRITY |
Integrity can be verified, but no sequencing or replay detection is enabled. Use this flag if you need to sign the data or to verify a signature. |
CONNECTION |
Connection-oriented context. Provides support for connection-based distributed applications. |
DATAGRAM |
Connectionless context. Provides support for datagram-based and DCE-style RPC-based distributed applications. See SSPI documentation [MICR00] for more information. |
Impersonation and Delegation
Impersonation is an important concept in client/server communication. A server application may run under a security context that has more privileges than the client requesting the service. Before servicing the request, such a server should make sure that the client has sufficient access rights for the requested operation.
One approach to client authorization is for the server to keep and to use its own per client authorization information to determine whether an operation can be performed by the client. This approach is difficult to implement and to manage. The second approach is to have the operating system determine whether a client is authorized to perform an operation, using a mechanism called impersonation.
Impersonation is the temporary change of the security context to use the security context of another principal. When a server impersonates a client before accessing a resource, the operating system checks access rights under the new-client-security context automatically. Moving the task of checking access rights to the operating system simplifies server applications quite a bit. There is no need to maintain separate per client authorization data and write application code to enforce access rights.
In order for a server to impersonate a client in SSPI, the client should specify the impersonation level that the server is allowed to use when servicing the request. Windows 2000 has the following four impersonation levels:
-
Anonymous-the server is not allowed to find any information on the client's security context.
-
Identification-the server can only authenticate the client but cannot use the client's security context for access checks.
-
Impersonation-the server can authenticate the client and use the client's security context for local access checks directly. The server can also pass the context to another server on the same machine.
-
Delegation-the server can authenticate the client and use the client's security context for local access checks (direct or via other servers). The server can also pass on the context to a remote server to request service on behalf of the client.
An SSPI client should use ISC_REQ_IDENTITY to limit the server to do only identi-fication and should use ISC_REQ_DELEGATE to allow for delegation. A server's request for delegation is never honored by the security package, as this attribute is controlled by the client. If neither ISC_REQ_IDENTITY nor ISC_REQ_DELEGATE is used, impersonation is granted by default.
A server that wants to use delegation should use SECPKG_CRED_BOTHin its call to the AcquireCredentialsHandle function. To do delegation, the server should impersonate the client after the authentication and go through the authentication step with the new server as a client. This is done by calling the InitializeSecurityContextfunction under the client's identity and going through the authentication loop, as explained earlier. If delegation is allowed in the new connection, the second server can also impersonate the original client and delegate the credentials exactly as the first server did. There is no limit to the number of delegations in such a scenario.
For delegation to work, you need to make sure that all of the following conditions are true:
-
Client account can be delegated.
-
Service log-on account can delegate.
-
Service host computer is trusted for delegation.
To make sure that the client account can be delegated, unselect the Account is sensitive and cannot be delegated option. To verify that the service log-on account can delegate, select the Account is trusted for delegation option. Both of these options are available in the properties of the account object in Active Directory. To make sure that the computer on which the service is running is trusted for delegation, select the Computer is trusted for delegation option in the properties of the computer object in Active Directory. Note that the last two settings should be true for all the services and computers that participate in delegation.
A security package that supports impersonation sets the SECPKG_FLAG_ IMPERSONATION flag in the fCapabilities field of SecPkgInfo. A security package that supports delegation sets the SECPKG_FLAG_DELEGATION flag in the fCapabilities field of SecPkgInfo. These capabilities can be obtained by using the package management functions. Delegation is only supported by the Kerberos package.