Customizing GINA, Part 1

by digipine posted Oct 28, 2017
?

Shortcut

PrevPrev Article

NextNext Article

ESCClose

Larger Font Smaller Font Up Down Go comment Print
Security Briefs
Customizing GINA, Part 1
Keith Brown


Code download available at: SecurityBriefs0505.exe (286 KB)


 
Over the years I've had many people ask me to write about GINA, the Graphical Identification and Authentication component that serves as the gateway for interactive logons. This month I'll begin my coverage of this topic to help you get started if you're tasked to build such a beast. I'll build a sample called KIOSKGNA, which is the simplest possible GINA implementation I could think of. Next time I'll introduce a sample called FULLGINA, a more fully featured GINA. The examples and code snippets presented here are in unmanaged C++, which is the most natural way to do GINA development these days.

 

What is GINA and Why Replace It?
GINA is the pluggable part of WinLogon that third parties may replace in order to customize the functionality or the UI of the logon experience in Windows®. By replacing GINA, you can choose the authentication mechanism Windows will use for interactive users. This is often useful for smartcard or biometric logons.
Let me state up front that replacing the GINA is an advanced technique that should not be taken lightly. You should only do this if you have no other choice (if, for example, you are implementing a new logon mechanism that Windows does not support).
Think for a moment about what GINA does. It collects credentials from local and remote users (the latter via Terminal Services) who want to establish an interactive logon on the machine. It then establishes a logon session for that user. If GINA were compromised, it could be used to steal plaintext user passwords, user biometric data, smartcard PIN codes, and so on. A compromised GINA could act as a back door, letting a certain normal user log in with administrative privileges. In short, if you replace GINA, be sure to put your best programmers on the job, and then you should carefully review their work.
One other thing: small and simple is best here. This component runs as SYSTEM, accepts input from local and remote users, and runs all the time. Therefore this is one piece of code that must be absolutely bulletproof.

 

Where to Start
As of this writing, you'll find very little documentation on customizing GINA. There are a couple of samples in the Platform SDK, GINASTUB, and GINAHOOK. GINASTUB simply loads MSGINA.DLL, the default GINA implementation that ships with Windows, and delegates all calls through to it. If you build and install this sample, you'll see no visible change on your system. Because GINASTUB wraps MSGINA, it can pre- and post-process each request. This simple technique might be enough to solve your problem, but it certainly won't help if you need to do something important and nontrivial like replace the login mechanism with biometric authentication.
There is also a second sample called GINAHOOK which looks exactly like GINASTUB, except that it goes one step further by hooking into the dialogs that MSGINA displays. Using this technique you can change the look of any of the default login dialogs, and even change their behavior to some extent. But the danger here is that you'll be relying on internals of MSGINA that are not documented. This includes simple stuff like IDs for dialog controls but also includes behaviors of the underlying dialog procedures. Be wary of going down this road if you need to support many different versions of the operating system, as your GINA may break when the next service pack is installed!
For most nontrivial GINA replacements, writing a custom GINA from the ground up is the best choice. But there aren't any examples of such a beast out in the wild (certainly none for free). I'm going to remedy this right now.
Given the complexity of the subject, I'll be presenting a couple of columns on the topic, and won't have the space to explore every dark corner of GINA development, but my goal is to give you a great head start if you need to build a GINA yourself. For further information, I'm setting up a GINA development Wiki where you can find (and contribute) additional information and sample code. Just look for Keith Brown's Wiki at pluralsight.com/wiki.

 

Secure Attention Sequence
Before diving in head first, let me explain a concept that you'll often run into. A secure attention sequence (SAS) is something that a user does to get the attention of the real operating system so that she can perform some secure action such as logging on, unlocking her workstation, or changing her password.
The SAS most users are familiar with is Ctrl+Alt+Del, which is trapped by the kernel. This helps thwart malware that may try to spoof the logon dialog and steal credentials from the user because when WinLogon receives a SAS notification, it literally switches away from the normal user desktop onto a secure desktop before prompting the user for credentials. Ever wonder where your taskbar and desktop go when you press Ctrl+Alt+Del? They're still there; they're just not the current desktop anymore. The goal is to train users to type their password only after they press Ctrl+Alt+Del.
Now a SAS isn't restricted to just Ctrl+Alt+Del. If you've got a biometric device that can signal a driver on the computer when it's in use, that could be used as a SAS instead (for example, a user placing her finger on a print scanner). Another common SAS is generated by a smartcard reader when a card is inserted or removed. When you write your own GINA, you get to decide which mechanism(s) will be used to generate a SAS. There's a function (WlxSasNotify) that you call in WinLogon when you detect a SAS or want to simulate one. Of course, you can tell WinLogon to use Ctrl+Alt+Del if you don't have anything more appropriate.

 

Structure of GINA
GINA is a DLL that WinLogon loads and calls at various times while the computer is running. It's a long-running DLL that is typically unloaded only when the machine reboots, so an absence of memory leaks is a feature!
A custom GINA must expose several entrypoints defined by Microsoft. I have listed these functions in Figure 1. A custom GINA will almost always maintain some internal state, and WinLogon will help you here by passing a void* as the first argument to those functions. You can decide what that pointer points to. In my sample, I will have it point to an instance of a class called Gina, where all the state and behavior of my custom GINA will be defined.
Now one thing that confuses people who are new to GINA development is that besides the Wlx* functions that the custom GINA must implement, WinLogon also exposes several functions that the GINA may optionally call (see Figure 2). Since these functions all have the same prefix, Wlx, it's easy to get them confused. A quick way to tell the difference is to look at the first parameter to the function. If it is HANDLE hWlx, this is a function that WinLogon exposes for GINA to call. If it is PVOID pWlxContext, that's a function that your GINA is supposed to implement.

 

WinLogon States
WinLogon can be in one of three states at any given time, and for the purposes of this column, I'll label them whenever I refer to them so they stand out clearly. The states are: LOGGED_OFF, which means that WinLogon has no current logged on user, LOGGED_ON, which means the user is currently logged on, and LOCKED, which refers to being logged on with workstation locked.
WinLogon will call some of the most important functions in your GINA when it transitions between these various states. Now that you know about SAS and the WinLogon state machine, let me show you GINA in action.
Figure 3 GINA State Diagram 
Figure 3 shows a more fleshed-out diagram of GINA states and transitions, and you'll want to refer to it as you read. It includes many of the major functions in your GINA that WinLogon will call, showing the normal sequence of events.

 

Initialization
When the machine boots up, once the operating system has initialized and started any services marked as automatic, WinLogon loads GINA and calls WlxNegotiate followed by WlxInitialize.
Negotiate is trivial to implement. It simply gives GINA and WinLogon a chance to verify each other's version so that things work smoothly later on. The version you choose determines how much functionality WinLogon will expect from your GINA, and also how much functionality you can expect from WinLogon. As of this writing, the most current version you can implement is 1.4, which provides a bit of extra support for Remote Desktop in Windows XP, so that's the version I'll use in my FULLGINA sample.
In Initialize, you will receive a handle (hWlx) that represents WinLogon, along with a pointer to a table of functions that your GINA may call. These are the Wlx* functions that WinLogon implements, the ones that take a HANDLE as their first argument. In order to call these functions later on, you'll need to store this handle and pointer somewhere. That's where the last argument comes in, pWlxContext, which is an out parameter that you pass back to WinLogon. The simplest approach is to point this at a data structure (class or struct) that you use to hold your GINA's state, which includes the handle and dispatch table pointer given to you by WinLogon. Figure 4 shows a very simple example of this.
The actual implementation in my sample is a bit more sophisticated than this, but this clearly demonstrates the idea. From now on, when WinLogon calls any of the functions in your GINA, pWlxContext will be passed as the first argument, so you'll always have access to your state. For example:
 
BOOL WINAPI WlxIsLogoffOk(PVOID pWlxContext) { return ((Gina*)pWlxContext)->IsLogoffOk(); }
 
By simply dispatching each call to the GINA class, I can simply add member variables to the class to hold any state my GINA needs. It's a very natural way to develop a GINA, and you can reuse the stub code on any project.

 

A Day in the Life of GINA
Once GINA is initialized, WinLogon will start running its state machine, calling functions exported from GINA as its state changes. After initialization, no user is logged on yet, so WinLogon will ask you to display a logon prompt by calling WlxDisplaySASNotice. A typical GINA pops up a familiar dialog that says "Press Ctrl+Alt+Del to log in" at this point.
When the user presses Ctrl+Alt+Del, or GINA calls WlxSasNotify to generate a SAS of some other type, WinLogon dismisses your dialog (more on this later), and calls the next function in your GINA: WlxLoggedOutSAS.
WlxLoggedOutSAS may seem like a funny name, but it completely describes what's going on in your GINA. You are currently in the LOGGED_OUT state, and you've received a SAS. Your job here is to authenticate the user who is trying to log on. My FULLGINA sample opens a dialog to retrieve the user's name and password, and calls LsaLogonUser to see if the password is valid. If it's not valid, I present an error message, then loop back around and ask for a user name and password once again. Once given a valid set of credentials, LsaLogonUser establishes a logon session for the user and returns a token handle to my code, which I then return to WinLogon.
WinLogon takes the token returned from the GINA and configures the access control list (ACL) on the default desktop to make it private for this user's logon session. No other logged on user is allowed to access this desktop, except for administrators and the operating system itself. Now that the default desktop is ready to go, WinLogon calls back into GINA through WlxActivateUserShell. GINA must now launch the user's shell and return to WinLogon.
It's interesting that in the WlxActivateUserShell call, WinLogon does not provide the user's token handle that your GINA gave it in the last step. This is an example of state that your GINA must maintain internally. Now WinLogon is in the LOGGED_ON state, and the logged on user is in control. Just for kicks, let's say the user presses Ctrl+Alt+Del. Can you guess the name of the function WinLogon will call?
If you guessed WlxLoggedOnSAS, you're right. Here, you will likely give the user a set of options. Figure 5 shows the typical dialog displayed at this point. It should look familiar. This dialog is a lot easier to implement than it looks because WinLogon provides most of the functionality for you. GINA just needs to tell WinLogon which option the user chose by returning one of several predefined constants from WlxLoggedOnSAS. Handling the change password request is the only thing that requires much programming effort, and if you're using passwords, you'll need this functionality elsewhere in the GINA anyway.
Figure 5 WlxLoggedOnSAS 
Let's say the user chooses to lock the workstation. At this point, WinLogon will call GINA's WlxDisplayLockedNotice. This is similar to the WlxDisplaySASNotice. You simply display a modal dialog box and wait for GINA to dismiss it.
When the user presses Ctrl+Alt+Del, WinLogon will call WlxWkstaLockedSAS, at which time you should ask the user to authenticate once again. The tricky part here is that your GINA needs to tell WinLogon whether this is the same user who originally locked the workstation and is returning to unlock it, or if it's someone else entirely. In the latter case, you'll verify that this new user is an administrator and ask if he would like to forcefully log off the current user, in which case you'll eventually see a WlxLogoff call into your GINA followed by a return to WlxDisplaySASNotify, asking the user to log in.
If the user simply unlocks her workstation, WinLogon will return to the LOGGED_ON state and once again the user will be in control of her desktop. Now keep in mind that the user might log off, shut down, or even lock her workstation without necessarily pressing Ctrl+Alt+Del and going through the GINA. For example, the LockWorkstation API or even ExitWindowsEx can be called from any application, including Windows Explorer. Of course, the GINA will be notified if the user does log off or lock the workstation, but it's good to keep in mind that not all of these user actions will be initiated via GINA.

 

GINA Modal Dialogs and Threads
Any modal dialog you show from GINA needs to be interruptible by WinLogon. There are several reasons WinLogon might need to interrupt GINA's user interface: the user might be idle for several minutes, a SAS could occur, a screen saver might kick in, and so on. WinLogon has a very simple way of dealing with this. Instead of calling the normal Win32® functions DialogBox, DialogBoxParam, and so forth, GINA instead calls equivalent functions exposed through WinLogon's dispatch table. WinLogon can then hook into the dialog box procedure and end the modal dialog whenever it needs to. So if you're using a framework like MFC to implement your dialog boxes, you'll need to tweak it a bit to call through WinLogon's APIs.
But if you're pulling in MFC or even something more sophisticated, you might ask yourself if GINA is the right place for such application frameworks. Remember that a good GINA is small, simple, and bulletproof. Try to keep the amount of code your GINA loads to an absolute minimum.
While I'm on the subject of user interface code, I want to point out that the GINA really is a GUI component. It's designed to be single threaded, at least as far as WinLogon is concerned. WinLogon doesn't expect to be called back on any thread other than the one it used to call into GINA in the first place. For example, if GINA detects a SAS from an external device, it shouldn't call WlxSaSNotify on some random thread. It should instead use WinLogon's thread to make the call. Your best bet is to use the thread that called into WlxDisplaySAS/LockedNotice to let WinLogon know that the user generated a SAS via your device and wants to log in or unlock the workstation. If you do have a secondary thread that receives the device notification, you can simply post a message to your dialog that's displaying the SAS or Locked notice. The UI thread can then safely call WlxSasNotify.

 

Deploying GINA
Once your GINA is built, you'll want to deploy it and test it out. I strongly recommend that you don't deploy the GINA to your development box. Use a separate machine to host your GINA for testing. My own setup relies on Virtual PC with its undo disk feature. If something goes really wrong and I end up in an infinite reboot loop (yes, this will happen to you at some point), I just close down the Virtual PC and discard changes. Another option is to jump into Safe mode to remove a misbehaving GINA.
To deploy the GINA for testing, just copy the DLL onto the target machine (it is appropriate to place it in the System32 directory), and update that machine's registry by adding a named value under WinLogon's key:
 
HKLM\SOFTWARE\Microsoft\Windows NT\CurrentVersion\WinLogon
 
The named value should be GinaDLL, of type REG_SZ, and its value should be the name of the DLL. Reboot the machine, and you'll find your GINA running when the machine boots back up. If you are trying to redeploy a new version of your GINA, you may find that the existing version is locked by the operating system and that you cannot overwrite the file. In this case, rename the GINA file on disk before copying the new one. Then you can simply delete the old GINA later.
If you get into trouble and need to restore the old GINA, just delete the GinaDLL key and remember that you can access the registry of any machine remotely, as long as you can be authenticated as an administrator on the target machine. Just bring up REGEDIT.EXE and choose File | Connect Network Registry.
When you have finished all your testing and are finally deploying for real, after copying your GINA DLL onto the user's disk, you should make sure to set the ACL on the GINA DLL to ensure that only administrators are allowed to modify the file. You won't get this level of protection by default simply by installing into the System32 directory.

 

Debugging GINA
Unless you're someone who regularly uses low-level symbolic debuggers, you won't enjoy debugging a custom GINA while it's running inside WinLogon. That's why I designed my sample so that it can be run outside of WinLogon, and I recommend you design yours the same way. I simply expose one extra entry point, called DebugGINA, into my GINA DLL. In non-debug builds, this function does absolutely nothing. In debug builds, however, I can use this entrypoint to drive the GINA through any scenario that I want to debug. The key to making this possible is to abstract your interface to WinLogon so that when debugging, you can simulate the WinLogon dispatch table by providing your own implementation. The standard way of doing this in unit testing circles is to use interfaces coupled with mock objects.
I use a very simple interface called IWinLogon that has methods for each of the functions my GINA needs to call in WinLogon. WlxDialogBox becomes IWinLogon::wlxDialogBox, for example. Then during normal operation, I use an implementation of IWinLogon that actually calls through WinLogon's dispatch table. During debugging, I substitute a mock implementation that in many cases can get away with doing absolutely nothing. My mock object's implementation of IWinLogon::wlxDialogBox, simply calls DialogBox. It's dirt simple, and it works like a charm.
Of course, you'll need a program to load your GINA and call that debug entry point, but that's easy. Here's a simple snippet of code that calls LoadLibrary and GetProcAddress to call the DebugGINA entrypoint. Just build something like this as an EXE and you can step right into your GINA from the debugger.
 
void main() { GetProcAddress(LoadLibrary("mygina.dll"), "DebugGINA")(); }
 
Now before you do this, you'll want to ensure that you're debugging the program while running as SYSTEM to simulate the WinLogon environment. When I'm doing a lot of GINA debug sessions, I keep a command prompt running as SYSTEM open, and just type DEVENV from there to launch my debugger, load the project, and trace right in.
How do you get a command prompt running as SYSTEM? One way to do it is to schedule an interactive job with CMD.EXE as the target application:
 
at 7:32pm /interactive cmd
 
Of course the time you choose should be sometime in the very near future, like one minute from now. This causes the scheduler service (which runs as SYSTEM) to launch the command prompt, which inherits the security context of SYSTEM. Your debugger will inherit that security context as well when launched from your SYSTEM command prompt, which you should title as such (type this into the command prompt after it starts):
 
title SYSTEM (DANGER, WILL ROBINSON)
 
The silly title is actually quite serious—you should close this command prompt when you're not debugging because SYSTEM has full control of everything on your machine, and mistyping in this command prompt could have serious consequences.
In the next installment of this column, I'll drill down into some of the more complicated aspects of GINA development such as calling LsaLogonUser to establish a logon, using CreateProcessAsUser to launch the shell, and other important topics. At the end of this series, you'll have a simple but fully featured GINA implementation to start working with.

 

Send your questions or comments for Keith to  briefs@microsoft.com.

 

Keith Brown is a co-founder of Pluralsight, specializing in developing and delivering high quality training for software developers. Keith's most recent book, The .NET Developer's Guide to Windows Security, is available here. Keith also keeps a web log and contact information at www.pluralsight.com/keith.
TAG •