Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles / Languages / C++

Winlogon using Mobile Disk

4.83/5 (25 votes)
30 Nov 2007CPOL6 min read 1   2.6K  
This is a full set of applications that can be used to logon to Windows system using mobile disk. No password typing.

Introduction

PCLock is a system to allow a user to use his/ her mobile disk to logon to Microsoft® Windows® operating systems. It works as a SmartCard and the user does not need to know the password to logon. All information required to logon is kept in a file in the mobile disk (User.Info). This file contains some information about the mobile disk. Copying the file to another disk does not work. User logs on to Windows by inserting the disk and logs out by removing the disk (optional).

Overview

PCLock is designed around the Win32 Winlogon. Winlogon is a component of the Microsoft® Windows NT®/Windows® 2000/Windows® XP operating system that provides interactive logon support. Winlogon is designed around an interactive logon model that consists of three components (as shown in Figure 1 below): the Winlogon executable program, a Graphical Identification, and authentication dynamic-link library (DLL)-referred to as the GINA-and any number of network providers.

Screenshot - winlgn.jpg
Figure 1.

Winlogon handles interface functions that are independent of authentication policy. The GINA is a replaceable DLL component that is loaded by Winlogon. The GINA implements the authentication policy of the interactive logon model, and is expected to perform all identification and authentication user interactions. For example, replacement GINA DLLs can implement smart card, retinal-scan, or other authentication mechanisms in place of the standard Windows NT/Windows 2000 user name and password authentication. Network provider DLL is not used in our case.

Security

Each client requires a security key at the time of setup. This key should be the same for a group of PCs. Several groups can exist but user can login only on the same group. If there is only one group (all PCs use the same key) everyone can login to every PC. The security key is kept with Windows. This key is required while creating the authentication disk. Every disk contains Windows user information and this is checked using Local Security Authority (LSA) of Windows.

The User.Info File

The KeyGen application is used to create User.Info file on mobile disk. The file created for a disk is unique and does not work on any other disk. The user can only use the disk as storage. Any change or format of the disk can cause the file invalid. KeyGen requires a security key while creating the disk that must be similar to the security key that is used while setting up the client. Validity of a disk can be checked using KeyGen. Windows user information is not checked here. Wrong information about logon causes a popup dialog box to appear asking for username and password. It is possible to use a domain. But this feature is not tested. All information on the disk is encrypted but it should be kept securely. Anyone having the disk can logon to client systems.

Authentication Process

When the computer boots up, the Winlogon initializes and negotiates with GINA. The GINA shows a dialog box and waits for the user to insert his/ her mobile disk (Figure 2).

Screenshot - WELCOME.jpg
Figure 2.

When a removable device is inserted into or replaced from a computer, a system wide WM_DEVICECHANGE message is broadcasted by the OS. The WM_DEVICECHANGE device message notifies an application of a change to the hardware configuration of a device or the computer. The GINA checks for a valid user information file. If found, it logs on to Windows using LogonUser API. The DevMon application monitors the WM_DEVICECHANGE message and if it finds no valid disk inserted into the system it sends a logout request to the Winlogon. If the application fails to quit properly, this may fail. Force termination of an application may cause data loss. So force is not applied. Users are suggested to logout the system manually from start button. DevMon is a helper application and it is optional.

Implementation

The application has four parts:

  1. The Custom GINA
  2. The KeyGen application
  3. The DevMon application
  4. The Setup application

Now let me give a short description for each of them.

Custom GINA

This is the heart of the project. It is used by Windows to interact with the user to manage a user session. It is a DLL with predefined functions. We make our GINA implementation to be used with mobile disk. Here I show the WlxLoggedOutSAS function that shows a dialog box (Figure 2) and waits for the user to insert the mobile disk that can be used to login that user.

C++
int WINAPI WlxLoggedOutSAS (
  PVOID                pWlxContext,
  DWORD                dwSasType,
  PLUID                pAuthenticationId,
  PSID                 pLogonSid,
  PDWORD               pdwOptions,
  PHANDLE              phToken,
  PWLX_MPR_NOTIFY_INFO pMprNotifyInfo,
  PVOID *              pProfile)
{
    if(MY_SAS_TYPE!=dwSasType)
        return WLX_SAS_ACTION_NONE;

    PGINA_CONTEXT pgContext = (PGINA_CONTEXT) pWlxContext;
      int ret = pgContext->pWlxFuncs->WlxDialogBox(pgContext->hWlx,
                                pgContext->hDllInstance,
                                (LPWSTR)MAKEINTRESOURCE(IDD_INSERT_CARD_NOTICE_DIALOG),
                                NULL,
                                DisplaySASNoticeDlgProc
                                );
    
    if(ret==IDCANCEL)
    {
        return WLX_SAS_ACTION_SHUTDOWN;
    }

    if (ret != IDC_LOGON_BUTTON) 
    {
        return WLX_SAS_ACTION_NONE;
    }

    TOKEN_STATISTICS userStats;
    TOKEN_GROUPS    *   pGroups;
    DWORD cbStats;

    if (!phToken)
        return WLX_SAS_ACTION_NONE;
 
    while(1)
    {

        if (!LogonUser(g_lpUserName,
                g_lpDomain, 
                    g_lpPassword,
                    LOGON32_LOGON_INTERACTIVE,
                    LOGON32_PROVIDER_DEFAULT,
                    phToken))
        {    
            //Logon failed. Give user a chance to update his disk
            ret = pgContext->pWlxFuncs->WlxDialogBox(pgContext->hWlx,
                    pgContext->hDllInstance,
                    (LPWSTR)MAKEINTRESOURCE(IDD_WIN_LOGON_DIALOG),
                    NULL,
                    UpdateWindowsUserDlgProc
                    );

            if (ret != IDOK) 
            {
                return WLX_SAS_ACTION_NONE;
            }
        }
          else
            break;
    }  

    pgContext->hUserToken=*phToken;

    //  Pass back null profile and options.
    *pdwOptions = 0;
    *pProfile =NULL;         
 
    // Get the authenticationid from the user token.
    if (!GetTokenInformation(*phToken,
                   TokenStatistics,
                (PVOID) &userStats,
                sizeof(TOKEN_STATISTICS),
                &cbStats)
    )
    {
        return WLX_SAS_ACTION_NONE;
    }
    else
    {
        *pAuthenticationId = userStats.AuthenticationId; 
    }

    DWORD size,i;
    pGroups = (TOKEN_GROUPS *)LocalAlloc(LMEM_FIXED, 1024);
        
    GetTokenInformation(*phToken,
        TokenGroups,
                pGroups,
                1024,
                &size);
        if (size > 1024)
           {
            pGroups = (TOKEN_GROUPS *)LocalReAlloc(pGroups, LMEM_FIXED, size);
            GetTokenInformation(*phToken,
                         TokenGroups,
                             pGroups,
                             size,
                             &size);
           }

        for (i = 0; i < pGroups->GroupCount ; i++)
           {
               if ((pGroups->Groups[i].Attributes & SE_GROUP_LOGON_ID) == 
                                                                SE_GROUP_LOGON_ID)
            {
                  CopySid(GetLengthSid(pLogonSid),
                           pLogonSid,
                           pGroups->Groups[i].Sid );
            break;
            }
    }

        LocalFree(pGroups);
    pMprNotifyInfo->pszUserName=DupMbToWsString(g_lpUserName);
    pMprNotifyInfo->pszDomain=DupMbToWsString(g_lpDomain);
    pMprNotifyInfo->pszPassword=DupMbToWsString(g_lpPassword);
    pMprNotifyInfo->pszOldPassword = NULL;
    return WLX_SAS_ACTION_LOGON;
}

The dialog procedure is as follows:

C++
int CALLBACK DisplaySASNoticeDlgProc(
    HWND    hDlg,
    UINT    Message,
    WPARAM  wParam,
    LPARAM  lParam)
{

    PDEV_BROADCAST_HDR pheadBC;
    PDEV_BROADCAST_VOLUME pVol;
    HWND hMsg=GetDlgItem(hDlg,IDC_STATUS_STATIC);

    CUserInfo ui;
    int drv;
    char lpPath[255];

    switch (Message)
    {

    case WM_DEVICECHANGE:
    Sleep(2000);

    pheadBC=(PDEV_BROADCAST_HDR)lParam;

    if(wParam==DBT_DEVICEARRIVAL && pheadBC->dbch_devicetype==DBT_DEVTYP_VOLUME)
    {
        pVol=(PDEV_BROADCAST_VOLUME)lParam;
        drv=ui.ValidDiskPresent();
        lpPath[255];

        if(drv)
        {
            sprintf(lpPath,"%c:\\%s",'A'+drv,DISK_FILE_NAME);
            USER_INFO user_info=ui.LoadUserInfo(lpPath);
            user_info=ui.Decode(user_info);

            strcpy(g_lpUserName,user_info.lpWindowsUser);
            strcpy(g_lpPassword,user_info.lpWindowsPassword);
            if(strlen(user_info.lpDomain))
                strcpy(g_lpDomain,user_info.lpDomain);
            else
            {
                strcpy(g_lpDomain,".");
            }
            g_nLoginType=user_info.dwLogonType;

            EndDialog(hDlg, ID_LOGON_SUCCESS);
        }
    }

    break;
    }
    return(FALSE);
}

Device Monitor Application

This application is used to monitor the mobile disk. If the user removes a mobile disk, the system sends WM_DEVICECHANGE message to the application. Now we check if it is the disk used to login user and if true, end user session by calling ExitWindows API.

C++
LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    int wmId, wmEvent;

    switch (message) 
    {
        case WM_DEVICECHANGE:
            if(wParam==DBT_DEVICEREMOVECOMPLETE)
            {
                if(!g_bLogoutOnDiskRemove) break;

                //MessageBox(0,"Device Removed",0,0);

                if(!g_UserInfo.ValidDiskPresent())
                    ExitWindows(EWX_LOGOFF,EWX_FORCEIFHUNG);
            }
            break;
        default:
            return DefWindowProc(hWnd, message, wParam, lParam);
   }
   return 0;
}

We need to check if a valid disk is still present on system to make sure the actual logon disk is removed.

C++
BOOL CUserInfo::ValidDiskPresent()
{
    TCHAR    drive[10], lpPath[100];
    DWORD dwDrives=GetLogicalDrives();
    for(int i=2;i<26;i++)
    {
        int bit=(int)pow((double)2,i);

        if(dwDrives&bit)
        {
            sprintf(drive,"%c:",'A'+i);            
            if(FIND_DRIVE_TYPE==GetDriveType(drive))
            {
                sprintf(lpPath,"%c:\\%s",'A'+i,DISK_FILE_NAME);
                if(!PathFileExists(lpPath))continue;

                USER_INFO user_info=LoadUserInfo(lpPath);
                user_info=Decode(user_info);
                if(CheckDisk(user_info,i))
                    return i;
            }
        }
    }
    return 0;
}

The KeyGen Application

This application is used to generate userinfo file on mobile disk. It takes input from the user. It shows a combo box of all users to select from. To enumerate users in a combo box, we use the following function.

C++
void CSelectUserDlg::EnumUsers()
{
   LPUSER_INFO_0 pBuf = NULL;
   LPUSER_INFO_0 pTmpBuf;
   DWORD dwLevel = 0;
   DWORD dwPrefMaxLen = -1;
   DWORD dwEntriesRead = 0;
   DWORD dwTotalEntries = 0;
   DWORD dwResumeHandle = 0;
   DWORD i;
   DWORD dwTotalCount = 0;
   NET_API_STATUS nStatus;

   char    *pmbbuf   = (char *)malloc( 100 );
   
   // Call the NetUserEnum function, specifying level 0; 
   //   enumerate global user account types only.
   //
   do // begin do
   {
      nStatus = NetUserEnum((LPCWSTR)pszServerName,
                            dwLevel,
                            FILTER_NORMAL_ACCOUNT, // global/domain users
                            (LPBYTE*)&pBuf,
                            dwPrefMaxLen,
                            &dwEntriesRead,
                            &dwTotalEntries,
                            &dwResumeHandle);

      if ((nStatus == NERR_Success) || (nStatus == ERROR_MORE_DATA))
      {
         if ((pTmpBuf = pBuf) != NULL)
         {
            for (i = 0; (i < dwEntriesRead); i++)
            {

                   if (pTmpBuf == NULL)
                   {
                    return;
        }

        wcstombs( pmbbuf, pTmpBuf->usri0_name, 99 ); 
        m_UsersCombo.AddString((char *)pmbbuf);
                   pTmpBuf++;
                   dwTotalCount++;
            }
         }
      }   
  
      if (pBuf != NULL)
      {
        NetApiBufferFree(pBuf);
    pBuf = NULL;
      }
   }
   while (nStatus == ERROR_MORE_DATA); // end do
  
   if (pBuf != NULL)
      NetApiBufferFree(pBuf);
}

The Setup Application

It stores both previous applications (excluding KeyGen) in its resource section. It extracts those on the client machine and creates registry settings as appropriate. Please refer to source code for details.

Installation

Use setup.exe to install on the client PC. Please be careful while entering the security key. If this key is wrong, you may not logon to Windows locally even if you are the administrator.

Create Logon Disk

Use KeyGen application to create the authentication disk. You may use this tool on any computer. But you can check the disk validity only where the PCLock is installed. Provide valid Windows logon user information when asked for. If you change the password on client computer, the user is asked for valid information when he/she inserts his/her disk. Invalid security key on the disk will cause logon failure. Remove the user.info file from root of mobile disk and the disk is no more valid. Do NOT format the disk or change the label of the disk. If you copy the same file it may not work. The user.info file is only valid for a single disk. The disk information is kept on the disk. You cannot copy the file to another disk even it is from the same manufacturer.

Uninstall

Logon to an administrator account and use setup.exe to uninstall PCLock. You need to provide the security key. After uninstall, restart the computer and run setup.exe again for optional clean up.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)