pGina Core Developer Documentation
The pGina Architecture
pGina’s architecture is based on three main components: the credential provider/GINA, the pGina service, and the plugins. The image below shows the components and their relationship to eachother.
The Credential Provider or GINA augments or replaces the default Windows authentication functionality. In Windows Vista or later, this component is called a Credential Provider (CP), in prior versions of Windows, we it is a GINA. More details on each of these options is provided below, suffice it to say for now that this component plugs-in to the Windows authentication system and can configure parts of what is requested (username/ password), and what is displayed (logo, MOTD, etc.). In a standard login, the CP/GINA receives the user’s credentials and passes them along to the pGina Service via a named pipe.
The pGina Service is the core of the pGina system. It recieves the credentials from the CP/GINA and then activates the plugins which are responsible for the authentication, authorization, and other login time actions. The pGina service does no authentication/authorization on its own. Instead, it is up to the plugins to do this work. The pGina service is essentially a “traffic cop” that invokes the plugins in their configured order and determines the overall result based on the results returned by the plugins.
The plugins and the pGina service are both written in C# on the .NET 4.0 platform. The CP/GINA is (and must be) written in unmanged C++. Communication between the CP/GINA and the service is done via a named pipe. The plugins are loaded at startup by the service and enumerated using reflection.
The Credential Provider
In Windows Vista and later, in order to provide alternate methods of authentication and/or authorization, one must implement a Credential Provider (CP). The CP can configure the UI that is presented to the user, as well as determine the result of the authentication. A CP is a COM object that is loaded and instantiated by the
LogonUI.exe process when a user is presented the opportunity to unlock, login, or otherwise interact with the
The pGina CP is defined within the main code distribution in the directory:
pGina/src/CredentialProvider. It consists of two main parts: the provider and the credential (naturally enough). The provider class implements
ICredentialProvider. In pGina, this class is named
pGina::CredProv::Provider (located in
Provider.cpp|h). The credential class implements
ICredentialProviderCredential and the pGina implementation is
pGina::CredProv::Credential located in
When initializing a login/unlock/CredUI scenario, the following steps occur:
- The provider object is told the scenario that it is running under via a call to
- The provider is asked about how many credentials it provides via a call to
GetCredentialCount. This equates to the number of tiles that you will see in the resulting UI. For example, the default credential provider usually displays one tile for each local user.
- The provider is then queried for a
ICredentialProviderCredentialobject for each credential via
GetCredentialAt. In this function, we create a
Credentialobject, initialize it, and provide it to the caller (via an output argument). The pGina CP only provides a single credential object.
The provider object also defines the number of fields that are presented to the user in the UI. A field is a UI element such as a text-box, image, label, or button, and each field has a name and other properties.
All credentials from a given provider (in a given scenario) have the same number of fields as provided by the function
GetFieldDescriptorCount. High level details about each field (name, id and type) are provided via
The credential object provides detailed information about the UI fields via the functions
The pGina Service
The Service is a “conductor to guid the orchestra through the login process”. Of course, it does more than that. Its responsible to handle events, catch exceptions, run the plugin chain and storing user data. Communication with plugins is done by pGina.Shared.Interfaces while the CP communicaton is done via a named pipe.
Shutdown events are catched in the ServiceBase.OnCustomCommand method. The service will inform any plugin registred for IPluginLogoffRequestAddTime Interface that a shutdown is going on and polling those plugins by calling LogoffRequestAddTime(), waiting for them to report false.
SessionChange events are captured with a hidden form and passed down to plugins by using the IPluginEventNotifications interface. The SessionChange Method is part of the Service class in pGina.Service.Impl.
If there is an unhandled ecxeption, CurrentDomain_UnhandledException method is called and the exception logged in the pgina log. The service will stop in such a situation. Exceptions from plugins are catched by the service without ending it and the plugin status is set to false.
The plugin chain is called with sessionDriver.PerformLoginProcess() from the HandleLoginRequest method in the Service class of pGina.Service.Impl. Before even doing that HandleLoginRequest will check if it is appropriate to call PerformLoginProcess(). If its an unlock scenario its not necessary also UAC stuff came into account. Its HandleLoginRequest who decide what need to be done with the information delivered from the CP, it also return the status and message of the login to the CP.
Userdata is stored in the private ObjectCache m_sessionPropertyCache in the Service class of pGina.Service.Impl. The key of the dictonary represents the sessionId while the value is a list of SessionProperties. The first entry in the SessionProperties list contains informations about the user who is logged into the session, any other entry is for UAC users. UAC users are programs started by “run as administrator” though the pgina UAC GUI. A lot of methods are reading and writing to m_sessionPropertyCache it is essential to lock this resource if you access it.
Communication with plugins is done by pGina.Shared.Interfaces. Depending on the plugin Interface assignment, information is transmited and received to and from plugins. Pipe communication with the CP is defined in Abstractions.Pipes and handled with HandleMessage in the Service class of pGina.Service.Impl. Its a minor communication, basically login status informations or the type of login that is requested.