Security Briefs: Explore the Security Support Provider Interface Using the SSPI Workbench Utility --

来源:百度文库 编辑:神马文学网 时间:2024/04/24 12:46:15




Explore the Security Support Provider Interface Using the SSPI Workbench Utility Keith BrownDownload the code for this article:Security0800.exe (135KB) T
his month I‘d like to talk about an interfacethat hasn‘t gotten nearly enough press, the Security Support ProviderInterface (SSPI). I have a utility, the SSPI Workbench, to help youlearn about SSPI and explore the various authentication protocols thatMicrosoft® Windows® 2000 supports. Let‘s dive right in, as there is alot to talk about.
Patterns in Secure Network Communication
Securenetwork communication generally follows a common pattern, where twoparties, often strangers to one another, introduce themselveselectronically over the network. For illustration, I‘ll call these twostrangers Alice and Bob. Just like in real life, this introduction ofAlice and Bob generally requires the participation of a trusted thirdparty. This third party is known as an authority—I‘ll call him Trent.(The names I use here are culled from Bruce Schneier‘s inspiring book, Applied Cryptography.)After the introductory handshake, Alice and Bob receive an indicationof who the other party purports to be, and if the handshake issuccessful, Alice and Bob develop trust in each other‘s identity.
OnceAlice trusts Bob‘s identity, she may want to send him data over thenetwork. Let‘s say Alice and Bob are communicating over a TCP/IPconnection, and that they are on separate subnets from one another sothere are routers in between. It‘s entirely possible for a bad guy(Mallory) to have hijacked a router between Alice and Bob, so thatafter Alice and Bob introduce themselves Mallory can start sendingpackets to Bob pretending to be Alice or vice versa. Mallory can alsosee all the packets traveling between Alice and Bob through thecompromised router, and can modify these packets at will.
Todefend against this sort of attack, during the introductory handshakeAlice and Bob both agree on a secret encryption key, known as a sessionkey. This key can be used to protect their communication and establisha secure channel over an otherwise public network. When Alice sendsdata to Bob, she can encrypt the entire contents of the payload,including a message sequence number. When Bob decrypts the payload andverifies that the resulting plaintext isn‘t gibberish and that theexpected sequence number is present, Bob trusts that the followingthree things are true: Mallory couldn‘t have forged the message,Mallory couldn‘t have decrypted the contents of the message to read it,and Mallory couldn‘t be replaying a message he recorded earlier. Thesethree assertions presuppose that Mallory doesn‘t know the session key.
Encryptionis expensive in terms of CPU cycles, so if Alice doesn‘t care aboutMallory reading the contents of her message to Bob, but instead wantsto protect against forgery and replays, she can protect the messageusing what is known as a Message Authentication Code (MAC).
Message Authentication Code
Conceptually,a MAC is quite simple. Imagine that Alice calculated a checksum of hermessage and appended that checksum to the end of the message beforesending the message to Bob. When Bob receives the message, he canverify the checksum. But what does this prove? It proves nothing.Mallory could have easily made changes to the message and simplyrecalculated and replaced the checksum. In fact, depending on thealgorithm used for the checksum, Mallory might have even been able tomodify the message without changing the checksum. So there are twofundamental flaws in this scheme.
Tofix the problem I just mentioned, Alice and Bob simply agree to use asophisticated algorithm and a reasonably long checksum to guaranteethat it‘s computationally infeasible to find another message with thesame checksum. Cryptographic digest algorithms such as MD5 and SHAexist for this purpose. To fix the first problem I mentioned, Alice canuse her session key to encrypt the checksum. This is much lessexpensive than encrypting the entire payload, and it effectivelyauthenticates the message (thus the term "Message AuthenticationCode"). When Bob receives the message and the MAC, he simply performshis own checksum, encrypts it with the session key, and compares thisMAC to the one Alice sent. If all goes well, Bob trusts that Mallorydidn‘t forge or replay the message. Bob trusts that this message istruly an authentic message from Alice, the party to which he wasintroduced earlier.
Network Authentication Protocols
Introducingtwo strangers over a public network and distributing a secret sessionkey that those two strangers can use to secure their communicationchannel is a tricky business, and there are many protocols for doingit. For Windows the mainstream protocols used include Kerberos, SecureSockets Layer (SSL), Windows NT® Lan Manager challenge-responseprotocol (NTLM), and the Secure Protected Negotiation protocol (SPNEGO)that allows Alice and Bob to negotiate the strongest authenticationprotocol that they both support while guarding against downgradeattacks from Mallory.
Eachof these protocols requires Alice and Bob to send certain bits ofinformation known as tokens to one another. In Kerberos, Alice sendsBob a ticket plus an authenticator, and Bob may or may not respond withhis own authenticator, depending on whether mutual authentication isrequired. SSL requires a four-way handshake, and typically providesmutual or server-only authentication via certificates. NTLMauthenticates Alice to Bob using a simple three-leg challenge-responsemechanism.

Figure 1 Kerberos versus NTLM
Tryingto support more than one of these protocols would be extremelychallenging, especially considering that these protocols often requirecommunication with Trent, the trusted authority who helps strangersintroduce themselves to one another electronically. Figure 1shows the difference in the authentication handshake between Kerberosand NTLM, as an example. These differences are even more striking whenAlice and Bob are in different domains (thus they have differentauthorities).
SSPI to the Rescue
SSPI is the Microsoft version of the Generic Security Service API (GSSAPI) standard that is documented in RFC 1508 and 1509 (http://www.ietf.org/rfc.html).The idea behind both of these interfaces is that network authenticationbetween two parties (such as Alice and Bob) generally follows a commonpattern.
Aliceand Bob need to exchange one or more bits of information in an orderedsequence, and since Alice and Bob may be communicating using one of anumber of network protocols (HTTP, RPC, SMB, IIOP, DCOM, Java RMI, andso on), it‘s wise to not hardcode a single authentication handshakeinto the network protocol itself. It‘s better to simply provide amechanism to piggyback authentication tokens on top of whateverconnection establishment handshake the network protocol alreadyrequires.
Forinstance, in DCE RPC (and thus in DCOM, which is ultimately derivedfrom DCE RPC), there is already a handshake used to synchronize theprotocol machines on the client and server. DCE RPC simply providesspace in this handshake for opaque, arbitrarily sized authenticationtokens. A large part of the job that SSPI and GSSAPI perform is thegeneration and evaluation of these tokens.
WhenKerberos is in use and Alice makes her first RPC or DCOM call to Bob,the Kerberos Security Support Provider (SSP) on Alice‘s machine will beasked to generate a token to send to the server. The token contains aticket and an authenticator proving Alice‘s ownership of the ticket.The server-side RPC runtime on Bob‘s machine takes this opaque tokenand simply hands it off to the server-side Kerberos SSP, which decryptsthe ticket and authenticator, discovers the client‘s identity, andgenerates a token that will prove Bob‘s identity to Alice, assumingAlice requested mutual authentication. Bob‘s RPC runtime sends thisback to Alice‘s RPC runtime, which hands the token to the client-sideSSP, which will return an error if Bob‘s token doesn‘t check out.
Ignoringthe details of the Kerberos authentication handshake, the idea is thatthe client and server can now mix and match network transports andauthentication protocols, which makes the system much more flexible.There are, however, a few exceptions to this rule. For instance, SSLauthentication can only be performed over a connection-orientedtransport. But even with these limitations, SSPI and GSSAPI are veryimportant technologies.
Asa WinInet, RPC, or COM programmer, you are shielded from the details ofthe SSPI interaction. But understanding how it works will help youunderstand and often debug problems in your application. Also, ifyou‘re programming raw sockets and you want to secure yourcommunication, it‘s imperative to understand SSPI.
I‘veproduced a rather sophisticated interactive workbench that you can useto experiment and learn about SSPI and the various authenticationprotocols that it abstracts. You can download the source from the linkat the top of this article. The sample code focuses on conventionalauthentication protocols such as Kerberos and NTLM and ignores SSLbecause it is so utterly different. I plan to write an article in thefuture that covers this, so stay tuned.
Selecting a Provider
Thefirst thing to realize is that the client and server need to agree on aprotocol. If the client and server want to use Kerberos, they both needto use the Kerberos SSP. If they each want to use NTLM, they both needto use the NTLM SSP. If the client and server want to negotiate aprotocol using SPNEGO, they both need to use the SPNEGO SSP. Providersare chosen by name—for instance, the names Kerberos, NTLM, andNegotiate are valid names of SSPs. SSPI provides a function calledEnumerateSecurityPackages so you can see which providers are supportedon the local machine and what their various capabilities include.
Once you‘ve decided on an SSP, you‘re ready to begin making calls to SSPI.
Selecting Credentials
Thefirst thing the client and server need to do is choose a set of networkcredentials with which to perform the authentication handshake. TheSSPI function used to select credentials is AcquireCredentialsHandle: #include SECURITY_STATUS AcquireCredentialsHandle( SEC_CHAR *pszPrincipal, // generally unused SEC_CHAR *pszPackage, // name of package ULONG fCredentialUse, // inbound/outbound PLUID pvLogonID, // logon identifier PVOID pAuthData, // SSP-specific data PVOID pGetKeyFn, // generally unused PVOID pvGetKeyArgument, // generally unused PCredHandle phCredential, // [out] handle PTimeStamp ptsExpiry // [out] lifetime );
The critical input parameters are pszPackage, whichrepresents the name of the SSP you want to use, and pAuthData, whichpoints to an SSP-specific data structure. The result of this call is anSSPI credential handle that abstracts the credentials you‘ve chosen.
Theoptional pAuthData parameter allows you to specify extra informationabout the credentials. On Windows 2000, this typically points to aSEC_WINNT_AUTH_IDENTITY_EX structure: typedef struct _SEC_WINNT_AUTH_IDENTITY_EX { unsigned long Version; // version of struct unsigned long Length; // size of struct LPTSTR User; unsigned long UserLength; LPTSTR Domain; unsigned long DomainLength; LPTSTR Password; unsigned long PasswordLength; unsigned long Flags; LPTSTR PackageList; // for SPNEGO unsigned long PackageListLength; } SEC_WINNT_AUTH_IDENTITY_EX;
Ifyou‘ve chosen the Negotiate package, you should specify acomma-delimited list of the real SSP names that you‘d like to negotiatewith your peer. You can also choose an arbitrary authority, principal,and password if you‘re using a conventional password-basedauthentication scheme like Kerberos or NTLM.
Ifyour process is running in the TCB, such as a service running in theSYSTEM logon session, you can choose to use the default credentials ofany logon session on the machine by passing the logon session LUID viathe optional parameter pvLogonID. So if Alice is logged ininteractively, you can borrow her network credentials if you know herlogon session LUID. This is pretty easy to discover by callingGetTokenInformation and asking for TokenStatistics; the logon sessionLUID is returned in the AuthenticationID field of the resulting datastructure. This should be a clear reminder to be careful about whichmachines you log into using your domain credentials.
ThefCredentialUse parameter indicates how the credentials are to be used.Clients should specify SECPKG_CRED_OUTBOUND, and servers should specifySECPKG_CRED_INBOUND.

Figure 2 Selecting Credentials
Finally, when you‘re finished using a credential handle, call FreeCredentialsHandle to release it. Figure 2 shows the user interface for selecting credentials in the workbench.
Selecting Context Requirements
Theclient and server need to decide what sort of expectations they havefor the authentication handshake before they perform it. For instance,the client might want to require mutual authentication, or to delegateher credentials to the server (Kerberos supports this). The client andserver also should indicate their desire for confidentiality andintegrity protection. The constants for the attributes I‘ve listed areas follows: #define ISC_REQ_DELEGATE 0x00000001 #define ISC_REQ_MUTUAL_AUTH 0x00000002 #define ISC_REQ_INTEGRITY 0x00010000
ForKerberos, the client will also need to determine the server principalname she thinks she‘ll be talking to. (Her SSP will need to obtain aticket for the server principal, so this is not optional, even if shedoesn‘t require mutual authentication.) Once the client makes thesedecisions, she‘s ready to initiate a handshake with the server.
The Handshake
Togenerate the first authentication token to send to the server, theclient must call InitializeSecurityContext. This is a generic functionthat is designed to process incoming tokens from the server andgenerate tokens to send back to the server, so it actually takes areference to two tokens: the incoming token that was read from theserver and the outgoing token being produced by the SSP to be sent tothe server on the next leg of the handshake.
Onthe first leg, the incoming token reference is NULL, as you‘d expect.The result of each call to InitializeSecurityContext is a contexthandle, which will ultimately hold a reference to the session keydiscovered during the handshake. This handle will later be used toencrypt or sign messages. SECURITY_STATUS InitializeSecurityContext( PCredHandle phCredential, // client cred handle PCtxtHandle phContext, // cur ctx handle SEC_CHAR *pszTargetName, // server principal ULONG fContextReq, // required attrs ULONG Reserved1, // must be zero ULONG TargetDataRep, // byte ordering PSecBufferDesc pInput, // incoming token ULONG Reserved2, // must be zero PCtxtHandle phNewContext, // new ctx handle PSecBufferDesc pOutput, // outgoing token PULONG pfContextAttr, // resulting attrs PTimeStamp ptsExpiry // ctx lifetime );
Notethat the first parameter is the credential handle the client selectedearlier. The second parameter, phContext, will be NULL the first timethe client calls this function, but for each subsequent leg the clientwill feed back the context handle generated from the previous call tothis function (this is produced via the phNewContext parameter). ThepszTargetName and fContextReq parameter are the server principal nameand the requested context requirements discussed earlier. The pInputand pOutput parameters point to buffer descriptors that ultimatelyreference the tokens being read and generated by the SSP.

Figure 3 Context Reqs
AsI mentioned earlier, pInput will be NULL on the client‘s first call tothis function. pOutput must reference a buffer for the SSP to write atoken into, and since this is client-managed memory, the client needsto determine how much memory to allocate for the token. The clientdetermines the maximum token size for a given SSP by calling an SSPIfunction named QuerySecurityPackageInfo. Figure 3shows the interface provided by the workbench for specifying contextrequirements and making calls to InitializeSecurityContext.
At this point, the client can transmit the token to the server. Figure 4shows the workbench interface for transmitting and receiving messages.(The workbench uses raw TCP and defaults to using the local hostaddress so both client and server can be run on the same machine.) Whenthe server receives the token, he calls AcceptSecurityContext to passthe token to his SSP. SECURITY_STATUS AcceptSecurityContext( PCredHandle phCredential, // server cred handle PCtxtHandle phContext, // cur ctx handle PSecBufferDesc pInput, // incoming token ULONG fContextReq, // required attrs ULONG TargetDataRep, // byte ordering PCtxtHandle phNewContext, // new ctx handle PSecBufferDesc pOutput, // outgoing token PULONG pfContextAttr, // resulting attrs PTimeStamp ptsTimeStamp // ctx lifetime );

Figure 4 Transmit/Receive
Aswith InitializeSecurityContext, the first parameter is the credentialhandle (for the server this time), and phContext will be NULL the firsttime the server calls this function. Later calls to this function willfeed back the previous context handle produced via phNewContext. ThefContextReq parameter is the server‘s requirements for the context (forexample, confidentiality and integrity), and the pInput and pOutputparameters represent the incoming token from the client and the newtoken produced to send back to the client.
Giventhis, it‘s clear how the client and server produce and consume thetokens used in the authentication handshake, but it‘s not clear howthey know how many legs of the handshake are needed. If you refer backto Figure 1, you‘ll notice that NTLMrequires a three-leg handshake. Kerberos requires a two-leg handshake,but only if the client requests mutual authentication. If the clientdoesn‘t care about this, Kerberos only requires one leg. The answer isin the return values from these functions, which will generally take onone of two success codes (or an error code if something failed) ifyou‘re using any of the built-in SSPs: SEC_E_CONTINUE_NEEDED SEC_E_OK
Thefirst status code indicates that another call to the function isrequired, and the second indicates that no further calls to thefunction are required. Figure 5 shows howthis works for Kerberos both with and without mutual authentication. IfAlice receives SEC_E_CONTINUE_NEEDED, this indicates that her SSPexpects at least one more token from Bob to complete the handshake. Thesame goes for Bob. When Alice receives SEC_E_OK, her SSP is indicatingthat it doesn‘t need any more tokens from Bob, but Alice needs tocarefully check the buffer descriptor referenced by pOutput to see ifher SSP wrote a token that needs to be sent to Bob. The same is truefor Bob.

Figure 5 The Kerberos SSPI Handshake
Theworkbench sample is basically a state machine that tracks the state ofa single SSPI association from either the client‘s or the server‘sperspective. (The workbench asks upon startup whether you want to actas a server or client.) For instance, when it comes time to callInitializeSecurityContext, the client‘s Initialize button will beenabled (see Figure 3). When you pressthis button, the application calls InitializeSecurityContext anddisplays the resulting token in a simple hexadecimal viewer (see Figure 6).Once you transmit this token, the viewer is cleared, and if your SSPexpects to receive a token from its peer, the Receive button will beenabled (see Figure 4). The workbench user interface will carefully lead you through all the steps necessary to establish a secure connection.

Figure 6 Viewing an Outgoing Token
As shown in Figure 5,it‘s clear that SSPI only produces tokens to be shuttled back and forthbetween Alice and Bob. But with Kerberos, Alice must go to Trent to geta ticket for Bob before she‘ll have anything to send to him in thefirst place. When does this happen? The answer is that the SSP doesthis automatically whenever necessary. When Alice uses SSPI, she‘s onlyconcerned with communication with her peer (Bob). It turns out that inAlice‘s first call to InitializeSecurityContext, the Kerberos SSP willsimply look at the server principal name specified via thepszTargetName parameter, and look up a cached ticket for Bob from thecredential cache specified via phCredential. If an appropriate ticketis not found, the SSP will synchronously retrieve a ticket from Trentand then add it to the cache during this first call toInitializeSecurityContext. Alice doesn‘t have to worry at all aboutthis detail.
If you‘re using the TktView component I presented inSecurity Briefsin the May 2000 issue, you can watch these tickets being added to yourcredential cache when you make your first call toInitializeSecurityContext for a server principal you have notauthenticated with recently (don‘t forget to refresh the view). If youcompletely flush your ticket cache first, you‘ll notice thatInitializeSecurityContext does two things: it gets a TGT so you cantalk to the ticket granting service, and then it gets a ticket for theserver. If you had never looked at your ticket cache, you never wouldhave known this magic was happening, which shows how good of anabstraction SSPI can be.
Similarly,on the final call to AcceptSecurityContext using the NTLM SSP, Bob‘sSSP will quietly perform pass-through authentication to communicate thechallenge and response to Trent for verification. You can see this ifyou‘re running a network trace utility such as NETMON.EXE.
Verifying Context Attributes
Afterthe handshake is complete, the pfContextAttr parameter to bothInitializeSecurityContext and AcceptSecurityContext will indicate theresulting attributes of the context to the client and server,respectively. This answers questions such as whether mutualauthentication was established, whether integrity and confidentialitycan be supported, and so on. If the client requested mutualauthentication, she should verify that this was achieved by checkingfor the ISC_RET_MUTUAL_AUTH flag. The workbench displays these flags(interpreted in English) in the dropdown list shown at the bottom of Figure 3.
Impersonating the Client‘s Security Context
Bob‘sfinal call to AcceptSecurityContext will establish a network logonsession for Alice on Bob‘s machine. The token associated with thislogon session will hold all of the authorization attributes for Alicethat make sense on Bob‘s machine. Universal and global groups wereinjected by the client‘s authority, domain local groups were injectedby the server‘s authority, aliases were injected by the server‘smachine, and, finally, privileges were injected from the server‘smachine. By calling the SSPI function ImpersonateSecurityContext (orthe more modern QuerySecurityContextToken), the server can obtain thistoken to perform access checks to validate the client‘s request. Notethat both of these functions require as input a server-side SSPIcontext handle previously established via AcceptSecurityContext. SECURITY_STATUS ImpersonateSecurityContext( PCtxtHandle phContext // preestablished ); SECURITY_STATUS QuerySecurityContextToken( PCtxtHandle phContext, // preestablished context HANDLE *phToken // put the token here );
Theserver-side workbench provides an option to impersonate the client oncethe handshake is complete. When you push the Impersonate button, theworkbench calls ImpersonateSecurityContext, and calls into a helper COMcomponent called the token dumper, producing an HTML-formatted reportthat shows the contents of the token.
Ina future column I‘ll return to complete this discussion, focusing onhow Alice and Bob can use the SSPI context handles they‘ve establishedto sign and encrypt messages to one another. The workbench is quitefunctional at this point. I‘ve only tested it with NTLM, Kerberos, andSPNEGO (with NTLM and Kerberos in the package list). If you want toplay with the other SSPs, you‘re on your own at this point. One funthing to do is use the MSN™ provider in a client workbench and ask itto prompt you for credentials. Please see the readme.htm file with theworkbench for instructions on its use, and notes on any known bugs thatI‘ve not yet worked out. Have fun!
Keith Brown works at DevelopMentorresearching, writing, teaching, and promoting an awareness of securityamong programmers. Keith authored Programming Windows Security (Addison-Wesley, 2000). He co-authored Essential COM, also published by Addison-Wesley.