Many people ask frequently on the Microsoft Newsgroups how can
they verify that a username and password match against the SAM. Well, on NT, at
least, it turns out there are two different methods you can use:
LogonUser(): It's easy to use, and gives you an
access token you can use for impersonation later, but unfortunately,
requires that the caller holds the SeTcb privilege, which poses a great
security risk. The usual workaround is to create an authentication server
that runs as a service under the SYSTEM account and does the real work of
calling LogonUser().
SSPI: This is the Security Service Provider
Interface, which provides a set of APIs you can use to authenticate a
user, and acquire credentials for it. The problem with using SSPI is that is
complicated, and some aspects of it are not that well documented. To make
matter worse, the SSPI samples on the KB and Platform SDK leave a lot to be
desired. The nice thing about SSPI, on the other hand, is that it's
transport-neutral, as you are responsible for transmitting the packets
between client and server (which might be in the same process). Also, some
providers support message encription.
So, how can you use SSPI, in a single process, to
authenticate a given username and password? Here it is step by step:
QuerySecurityPackageInfo()
to obtain a SecPkgInfo
pointerFreeContextBuffer()
).
AcquireCredentialsHandle()
once. For the client, you need to do that,SEC_WINNT_AUTH_IDENTITY
struct and pass a pointer toAcquireCredentialsHandle()
as the 5th parameter. Keep in mind that youInitializeSecurityContext
(for the client side) followed byAcceptSecurityContext
(for the server side), passing theSEC_E_OK
AcceptSecurityContext()
returns SEC_E_OK, you still have toImpersonateSecurityContext()
will cause you toRevertSecurityContext()
.One important thing to notice while you are calling InitializeSecurityContext()
and AcceptSecurityContext()
is that one of those returns SEC_I_COMPLETE_NEEDED
or SEC_I_COMPLETE_AND_CONTINUE
, you need to call CompleteAuthToken().
If you are using SSPI in server/client applications, you should be aware of what I
consider a rather annoying problem with SSPI: The SSPI client is not notified of the
authentication results, and rather always returns SEC_E_OK
(unless another
error pops up) in the last round, even if the server later denies the logon request. This means
you need to make your server trap the return of the last call to AcceptSecurityContext()
and explicitly tell your client if the logon request was accepted or not.
I've built a C++ library that greatly simplifies the use of SSPI, while
keeping it transport-independent, giving the programmer control of the buffers.
Included in the package are:
Minor Comment: The library is very much oriented toward creating
authentication services built on top of NTLM or Kerb, so currently you won't be
able to (easily) support things like SCHANNEL. That's one of my goals for the
next version, but I really haven't had the time to sit down and start version
2.0
Get the file here: wsspi.zip
Update: WSSPI V2.0 development is pretty far ahead, so you should see it in a
couple of weeks. Currently, I'm testing the library implementation, and adding SCHANNEL support
to it. Here's some of the things you can expect:
EncryptMessage()
andMakeSignature()
Update 08/01/2001: There are finally two updates to WSSPI. The first one is courtesy of
Jim Johnson, which added support to the basic WSSPI 1.0 for message signing and verification. You can
get this version here.
Since I've been meaning to finish WSSPI 2.0 for so long, and have not posted anything, I decided to
post what's already built in case someone finds it useful. Note that this is a complete rewrite, from
scratch of WSSPI, called WSSPI 2.1, but it is unfinished. Mostly, the Kerberos support is broken.
You can get the file here