Implementing an LSA proxy authentication package

LSA authentication packages are a part of the core security ecosystem in Windows NT. LSA APs are provided with credentials by logon applications, such as Winlogon, authenticate these credentials and provide the logon application with a logon session handle if authentication was successful. Two authentication packages provided with Windows are of special interest. MSV1_0 authenticates user credentials against the local SAM database or against a domain controller through the legacy logon protocols. Kerberos.dll uses the Kerberos protocol with modern domain controllers (or third party KDCs, etc.) for establishing logon sessions.

Applications that want to provide value-added logon functionality wish to become involved in the logon process. Winlogon provides the GINA facility before Windows Vista and the new Credential Providers in Windows Vista and later to allow involvement in the end-user logon experience.

While customizing the logon process is done at the logon application side with GINAs or CPs, sometimes customization is required at the authentication side, in the Local Security Authority subsystem. For instance, a fingerprint reader may customize the logon UI to allow fingerprint scanning as a means of authentication. The customized logon application will call LsaLogonUser with the fingerprint scanner’s custom LSA authentication package as the desired authenticator. The fingerprint LSA AP will scan a fingerprint database it maintains to perform the authentication process. That would be an independent LSA authenticaion package.

There are other scenarios in which the regular MSV1_0/Kerberos based logon authentication process is desired, but special pre or post-processing is required. In Windows XP, the problematic GINA architecture makes replacing the GINA cumbersome, as multiple third-party applications may attempt to install conflicting GINA replacements. Indeed, Microsoft documentation has been modified retroactively to recommend GINA hooks and stubs in lieu of outright replacement of the MSGINA implementation previously suggested.

With the ability to instrument the logon application at the GINA side limited and version dependent (a totally different approach is required for Vista’s CPs), the alternative of instrumentation at the LSA side warrants exploration. We can implement an LSA proxy authentication package. Such a package would appear to the LSA server as an ordinary package, but would be implemented by delegating to original APs like MSV1_0 and Kerberos, providing it with monitoring and instrumentation capabilities.

LSA authentication packages are loaded by LSASS at system startup by enumerating them from the registry value HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\Lsa\Security Packages. Both LSA authentication packages (APs) and security providers (SSPs) are registered at that location. For the purposes of this post, we’ll ignore pure SSPs and focus only on APs. We will register our proxy authentication package instead of MSV1_0 (and possibly Kerberos) at this location, and leave loading of the original APs to our proxy AP implementation.

Let us examine the system’s core APs, MSV1_0 and Kerberos:

C:\WINDOWS\system32>dumpbin /exports msv1_0.dll
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file msv1_0.dll
File Type: DLL
Section contains the following exports for msv1_0.dll
00000000 characteristics
48025C84 time date stamp Sun Apr 13 22:18:28 2008
0.00 version
1 ordinal base
32 number of functions
17 number of names
ordinal hint RVA name
5 0 0000175C LsaApCallPackage = _LsaApCallPackage@28
6 1 00014BC8 LsaApCallPackagePassthrough = _LsaApCallPackagePassthr
ough@28
7 2 00014A59 LsaApCallPackageUntrusted = _LsaApCallPackageUntrusted
@28
8 3 0000BDBB LsaApInitializePackage = _LsaApInitializePackage@20
9 4 0000F7FE LsaApLogonTerminated = _LsaApLogonTerminated@4
10 5 00007498 LsaApLogonUserEx2 = _LsaApLogonUserEx2@64
11 6 0001A7E5 Msv1_0ExportSubAuthenticationRoutine = _Msv1_0ExportSu
bAuthenticationRoutine@40
12 7 0001A7C6 Msv1_0SubAuthenticationPresent = _Msv1_0SubAuthenticat
ionPresent@4
13 8 00016E63 MsvGetLogonAttemptCount = _MsvGetLogonAttemptCount@0
2 9 0001B704 MsvIsLocalhostAliases = ?MsvIsLocalhostAliases@@YGHPAU
_UNICODE_STRING@@@Z (int __stdcall MsvIsLocalhostAliases(struct _UNICODE_STRING
*))
14 A 00016E59 MsvSamLogoff = _MsvSamLogoff@12
15 B 0000A7BA MsvSamValidate = _MsvSamValidate@52
16 C 00016E6E MsvValidateTarget = _MsvValidateTarget@12
1 D 0000E415 SpInitialize = _SpInitialize@12
32 E 00006BC2 SpInstanceInit = _SpInstanceInit@12
3 F 0000F08F SpLsaModeInitialize = ?SpLsaModeInitialize@@YGJKPAKPAP
AU_SECPKG_FUNCTION_TABLE@@0@Z (long __stdcall SpLsaModeInitialize(unsigned long,
unsigned long *,struct _SECPKG_FUNCTION_TABLE * *,unsigned long *))
4 10 00006AE0 SpUserModeInitialize = ?SpUserModeInitialize@@YGJKPAKP
APAU_SECPKG_USER_FUNCTION_TABLE@@0@Z (long __stdcall SpUserModeInitialize(unsign
ed long,unsigned long *,struct _SECPKG_USER_FUNCTION_TABLE * *,unsigned long *))
Summary
2000 .data
2000 .reloc
2000 .rsrc
1D000 .text
C:\WINDOWS\system32>dumpbin /exports kerberos.dll
Microsoft (R) COFF/PE Dumper Version 9.00.30729.01
Copyright (C) Microsoft Corporation. All rights reserved.
Dump of file kerberos.dll
File Type: DLL
Section contains the following exports for Kerberos.dll
00000000 characteristics
48025C4A time date stamp Sun Apr 13 22:17:30 2008
0.00 version
1 ordinal base
32 number of functions
10 number of names
ordinal hint RVA name
5 0 00026F8A KerbCreateTokenFromTicket = _KerbCreateTokenFromTicket
@44
2 1 00025773 KerbDomainChangeCallback = ?KerbDomainChangeCallback@@
YGXW4_POLICY_NOTIFICATION_INFORMATION_CLASS@@@Z (void __stdcall KerbDomainChange
Callback(enum _POLICY_NOTIFICATION_INFORMATION_CLASS))
6 2 000018B0 KerbFree = _KerbFree@4
7 3 00020A71 KerbIsInitialized = _KerbIsInitialized@0
8 4 00020A7C KerbKdcCallBack = _KerbKdcCallBack@0
9 5 0000380B KerbMakeKdcCall = _KerbMakeKdcCall@32
1 6 00013CAD SpInitialize = _SpInitialize@12
32 7 0000EDDF SpInstanceInit = _SpInstanceInit@12
3 8 000151DE SpLsaModeInitialize = ?SpLsaModeInitialize@@YGJKPAKPAP
AU_SECPKG_FUNCTION_TABLE@@0@Z (long __stdcall SpLsaModeInitialize(unsigned long,
unsigned long *,struct _SECPKG_FUNCTION_TABLE * *,unsigned long *))
4 9 0000ED1E SpUserModeInitialize = ?SpUserModeInitialize@@YGJKPAKP
APAU_SECPKG_USER_FUNCTION_TABLE@@0@Z (long __stdcall SpUserModeInitialize(unsign
ed long,unsigned long *,struct _SECPKG_USER_FUNCTION_TABLE * *,unsigned long *))
Summary
2000 .data
3000 .reloc
3000 .rsrc
43000 .text

Both DLLs export the SpInitialize function used for SSP initialization and for AP initialization export SpLsaModeInitialize and SpUserModeInitialize, called in LSASS context and in logon application context by secur32.dll, respectively.

As can be seen in MSV1_0’s export table, it provides a subauthentication package facility for those interested in becoming involved in the logon process. Clearly, this is the facility Microsoft intended third-parties to use, rather than creating proxy authentication packages. Unfortunately, subauthentication packages only provide a subset of authentication package functionality, primarily failing to provide access to the interactive logon (as opposed to the network logon) process. You should carefully consider your requirements. If they can be met with a subauthentication package, it is best to opt for that approach as it is far more likely to remain intact in Windows 7 and beyond. That said, the proxy authentication package model is given some legitimacy by this diagram, illustrating delegation to MSV1_0 by a hypothetical third-party custom authentication package.

It is interesting to note that MSV1_0 chooses to export the various LSA AP functions, such as LsaApLogonUserEx2, etc., via its DLL export table, while Kerberos opts not to export them, though quick examination of Kerberos.dll’s public symbols clearly illustrates the presence of the AP functions. So how does the LSA server know how to find and call the various AP functions? They are provided in a function dispatch table filled by the AP at initialization time, when SpLsaModeInitialize or SpUserModeInitialize are invoked (depending on the context of the AP’s use).

Keeping in mind that MSV1_0 was around before Kerberos was added to Windows NT in the Windows 2000 release and therefore may have remaining legacy cruft, reviewing the disassembly of the LSA server DLL, LSASRV, reveals that LSA first calls the Initialize functions to let the AP fill the function dispatch table, but then promptly examines the AP’s export table with GetProcAddress in the aptly named lsasrv!BindOldPackage function. This function appears to be invoked for the AP even when the function dispatch table has been filled. However, empirical evidence appears to suggest that the function dispatch table takes precedence over the contents of the export table. Indeed, Kerberos opts to do away with the export table entirely.

It appears that the export-based model was replaced with the dispatch table based model at some point during the lifetime of Windows NT. The export model has an obvious disadvantage of allowing a single DLL to implement only a single authentication package, a restriction will come back to later. The dispatch table model, on the other hand, allows the initialization function to return multiple dispatch tables, for multiple authentication packages, from a single DLL. This may yet prove useful for a proxy package.

In order to explore the feasibility of creating a proxy LSA authentication package, I wrote a test DLL, dubbed “LsaPrxAp”. This DLL provides the SpInitialize, SpLsaModeInitialize and SpUserModeInitialize exports. When invoked, it loads msv1_0 if it is not yet loaded. The DLL’s Initialize functions call the original msv1_0’s Initialize functions and then hook the authentication package by replacing function pointers with pointers to implementations of the LSA AP functions residing in LSAPrxAp. The original function pointers are saved in a global data structure so that LSAPrxAp’s stubs can invoke them while having value-added processing during the logon sequence. Since the name of the authentication package is the one provided in the SpLsaModeInitialize function by the AP, rather than the DLL’s name, it is easy to proxy the MSV1_0 module and become involved in the logon process. I did not bother with supporting Kerberos in my test, but hypothetically the proxy AP can call the Initialize functions for both MSV1_0 and Kerberos and return two dispatch tables as the parameter model for it allows.

Proxying a known authentication package of a known revision is one thing, but a more generic approach warrants special consideration. In both XP and Vista, the APs provide the latest LsaApLogonUserEx2 function. However, it is preceeded by LsaApLogonUserEx and LsaApLogonUser. The documentation for AP developers seems to indicate it is still legitimate to only support one of the older variants. Therefore, the proxy should take note that NULL function pointers in the dispatch table provided by the original APs are possible and should react accordingly.

As I investigated the behavior of the LSA server with respect to the export-based model for locating LSA AP functions vs. the function dispatch table model, I considered the problem of implementing a generic proxy if the dispatch table model was not preferred over the export table. The proxy AP would have to load the original AP and only have an LsaApLogonUserEx2 function if the original AP has. If the original AP has no LsaApLogonUser function exported, neither should the proxy AP. This presents special complication since compiling a different proxy AP for every possible set of exports is not feasible. I reiterate that the problem is theoretical since the function dispatch table model has no such fallacy.

In order to deal with this issue, I devised an approach where the proxy AP would initially export all possible LSA AP functions. At runtime, as the actual exports of the original AP is detected, functions that are missing from the original AP can be unexported from the proxy AP. I discuss the technique of unexporting functions through name obfuscation in the previous post.

Successfully compiling an LSA AP DLL required numerous acts of header juggling. For reference purposes, I provide the sequence I used:

#include <ntstatus.h>
#define WIN32_NO_STATUS
#include <Windows.h>
#include <NTSecAPI.h>
#define SECURITY_WIN32
#include <SSPI.h>

#ifndef NT_SUCCESS
#define NT_SUCCESS(Status) ((NTSTATUS)(Status) >= 0)
#endif

// LsaApCallPackageUntrusted is partially missing from NTSecPkg.h
typedef NTSTATUS
(NTAPI LSA_AP_CALL_PACKAGE_UNTRUSTED)(
	__in PLSA_CLIENT_REQUEST ClientRequest,
	__in PVOID ProtocolSubmitBuffer,
	__in PVOID ClientBufferBase,
	__in ULONG SubmitBufferLength,
	__out PVOID* ProtocolReturnBuffer,
	__out PULONG ReturnBufferLength,
	__out PNTSTATUS ProtocolStatus
);

The WIN32_NO_STATUS definition is required to prevent Windows.h from defining the NTSTATUS values. Letting ntstatus.h define them is required because Windows.h only defines a subset of the required values. SSPI.h requires choosing between Win32, Kernel-mode and Macintosh Classic (heh…) usage, resulting in the SECURITY_WIN32 preprocessor definition. The NTSecPkg.h header included with the Windows SDK I’m using makes the embarrassing error of missing the prototype for LsaApCallPackageUntrusted, while curiously attempting to provide the function pointer type for it.

It is wise to avoid loading the original AP DLLs during DllMain, due to potential loader lock issues. The first functions invoked after an AP DLL has been mapped to a process are those that fill the function dispatch table; SpInitialize for the SSP case, SpLsaModeInitialize for the LSASS-side AP case and SpUserModeInitialize for the secur32.dll logon application (Winlogon) side case. All three functions ought to load the original AP if required and perform any required hooking of the dispatch tables, pointing to your own proxy functions.

The initialization functions should also extract the function pointers for the original AP implementations and preserve them in global data. This can later be used to forward calls to the original implementation. Consider the implementation of LsaApCallPackage in my LsaPrxAp test DLL:

NTSTATUS NTAPI LsaApCallPackage(
	__in PLSA_CLIENT_REQUEST ClientRequest,
	__in PVOID ProtocolSubmitBuffer,
	__in PVOID ClientBufferBase,
	__in ULONG SubmitBufferLength,
	__out PVOID* ProtocolReturnBuffer,
	__out PULONG ReturnBufferLength,
	__out PNTSTATUS ProtocolStatus
)
{
	OutputDebugString(TEXT("lsaaprxap LsaApCallPackage\n"));

	return g_LsaPrxApFunctionTable.pLsaApCallPackage(
		ClientRequest, ProtocolSubmitBuffer, ClientBufferBase,
		SubmitBufferLength, ProtocolReturnBuffer, ReturnBufferLength,
		ProtocolStatus);
}

Obviously a real-world proxy AP would perform required processing before or after forwarding to the original function pointer preserved in global data, as well.

Hopefully this primer on implementing an LSA proxy AP will prove to be a welcome addition to Microsoft’s sparse documentation on LSA authentication packages.

Advertisements

21 thoughts on “Implementing an LSA proxy authentication package

  1. Hi, Koby,

    After reading this blog, I think it is possible to create a proxy authentication package to obtain the username and password then pass them to a third-party authentication server. Can you give some hints how to implement it? I only care to get the credential when the login type is “Network”.

    Mike

  2. Mike, if you only care about network logon sessions, consider using Microsoft’s documented approach, MSV1_0 subauthentication packages. Their primary disadvantage is that they do not support interactive logon sessions, which appears not to be a concern in your case.

    If you still opt for a proxy AP, note that the password buffer received by the AP callback may be “scrambled” by an internal, undocumented API. You may find more information about this elsewhere on the Web.

    If you have any other questions, let me know and hopefully I’ll be able to help out, if my other obligations allow for it.

    • How can i get the user input details in plain text format inside my msv1_0 sub authentication package to provide extra validation?

  3. Koby, I am very delighted that you replied very quickly.

    I am doing research about strong user authentication with RPC over HTTP services, which is claimed “impossible” by some experts (http://blogs.isaserver.org/pouseele/2007/02/)

    I believe the RPC request will be passed to domain server, if I can intercept it before authentication, then there is a possibility.

    As far as I know, using MSV1_0 subauthentication package is too late. Also it will only be invoked when the credentials are right. In strong authentication, people use OTP instead of password, or put them together. Obviously, these credential info doesn’t match the one regitered on server.

    Writing a whole custom AP could be a solution, but I still believe a proxy one is enough.

  4. Mike, let me make sure I understand your scenario correctly:

    Since the HTTP transport for RPC does not provide a means for OTP-based authentication token input (unlike other HTTP user agents with support OTP, etc., via the interactive FBA facility) you want to insert a proxy AP on the client side to do whatever authentication process is required. This proxy AP will return a token to the HTTP transport of RPC, which will promptly provide it to the server (via Basic or NTLM authentication supported by the transport) and the RPC server application will use that which was transmitted (e.g. a Kerberos ticket) as the means of authentication. Is this correct?

  5. Hi, Koby,

    Ideally, no proxy AP (or any other additional software) on the client side. People still use their normal MS Outlook, in the password field, OTP is filled with/without the original password.

    The proxy AP should be installed on DC server only I assume.

  6. Koby,

    Can you publish the code of your basic proxy ?

    I’m trying to write my own authentication package with no success. To debug it, I tried to write my own proxy to watch what is the difference between my output and msv1_0’s.
    However, I’m unable to make it work, even if this stub does nothing.

    for me, it’s the sign of a typo error. If you publish your code, this will help me to do this job faster.

    Thanks in advance

    Regards,
    Vincent

  7. Hello,

    Did you know that this blogpost is the only webpage in entire web that describes creating LSA proxy/delegates? =)

    Thanks for sharing your success!

  8. Thanks for the post!
    For those who are in research: the windows 2000 source code available on the torrents can be used to research msv_sspi (msv1_0.dll source code) module to how to deal with security structures and pointers for example.

  9. Thanks for the post Koby. I’m trying to wrap my head around subauthentication packages and keep coming up confused. Basically I’m trying to establish is the following is true.

    1. I could create MSV1 an Kerberos sub-authentication packages and register them on domain controllers to filter users logging onto the domain on thier workstations by additional critera without having to modify the client side.

    I’m not sure what is meant by sub-authentication packages not working on interactive logons. Does this mean what I’m trying to do will not work?

    • So I found a little bit more the SECURITY_LOGON_TYPE enumeration documentation on MSDN. Are you saying that subauthentication does not work for any of the Interactive logon types? If so then I’ll be looking at the proxy method you mention above.

      Thanks Again.

      • As documented, subauthentication packages do not work with interactive logons. To clarify, those would be e.g. domain logons in the Welcome screen to the local machine or a Remote Desktop session to a terminal server. Logging on for the purpose of accessing a file share would be a non-interactive logon.

        If intervening in interactive logons is a requirement of your solution, it appears there is no recourse but to use the proxy method. There is no need to elaborate that it relies on undocumented semantics, may be fragile across Windows versions and so on. Good luck.

  10. I’ve been implementing a proxy authentication provider and ran into an issue that I was wondering if you ran into as well.

    That is – if I install my proxy, everything works, except, any attempt to join a domain fails with an “access denied” error. However, if I replace msv1_0 with my proxy (calling it msv1_0, and defeating WFP) and call the original msv1_0 something else, it works fine… (This seems to be only an issue w/ msv1_0, not kerberos).

    • I haven’t tested a domain join with my proxy and I don’t have its sources around, unfortunately, so I can’t tell whether it also had the access denied issue or not.

    • I have found the same problem with a kerberos proxy, once you try to join any domain or use the function GetUserNameEx it will fail with “access is denied”. One way that apparently can solve this problem in Vista and Seven is to sign the proxy DLL with a valid certificate.

      I have also found that Windows XP is more sensitive to proxy kerberos packages. I can not get my signed kerberos proxy to run on Windows XP. I get a lot of “access is denied” errors from userinit.

      One interesting thing is that if I have a kernel debugger attached everything works without any problem. Is it possible that secur32.dll disables some security mechanisms in “attached debug” mode?

      Regards,
      Joel

  11. Hi KK,
    can u please publish the source code for your proxy authentication package.

    Additionally, assume if the user information is present on the remote server, and when we need to populate user information (such as NSS in Linux), how do we do that for windows?

    Thanks and Regards,
    Siddharth gupta

  12. Pingback: Implementating a SSA/AP proxy - Wings of Hermes – Berin's Infosec Blog

  13. Hi,
    I wonder that if it is possible to implement my own authentication without wrapping MSV1_0 by the proxy authentication package.

    MSV1_0 checks user information in SAM database which I can’t control, so I want to use my own authentication to identify the user (may simply store the user information in C:\system32 for checking during windows logon)

    (Actually, I simply tried to only return “STATUS_SUCCESS” in LsaApLogonUserEx2 funtion, however it won’t let me logon.)

  14. Hello KK,

    The blog is helpful and is almost only blog talking SSP/AP except the SDK document.
    I follow your blog create proxy, and implement lsalogonuser2 by myself. The issue is for local user I can easy verify in local SAM database. But for domain logon, where I can send the request to DC that interactive with custom SSP/AP?

    Thanks and Regards,

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s