/***********************************************************************
 *  File: lcredmgr.cpp
 *  Author: Todd Throne (tthrone@novell.com)
 * 
 *  Abstract: Implements the credential manager.
 * 
 *  Copyright (C) 2004 Novell, Inc.
 *
 *  This library is free software; you can redistribute it and/or
 *  modify it under the terms of the GNU General Public
 *  License as published by the Free Software Foundation; either
 *  version 2 of the License, or (at your option) any later version.
 *
 *  This library is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *  Library General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public
 *  License along with this library; if not, write to the Free
 *  Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 *    Following are registry entries, which must be created in order
 *    to run this credential manager;
 *
 *    [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\NetworkProvider\Order
 *       "ProviderOrder"=LCredMgr,(original string)
 *    [HKEY_LOCAL_MACHINE\System\CurrentControlSet\Services\LCredMgr\networkprovider
 *       "Class"=dword:00000002
 *       "Name"="LoginCapture Credential Provider"
 *       "ProviderPath"="
 *
 *    Following are registry entries, which must be created in order
 *    to run this notification dll;
 *
 *    [HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Winlogon\Notify\LCredMgr]
 *       "Asynchronous"=dword:00000001
 *       "DLLName"="LCredMgr.dll"
 *       "Impersonate"=dword:00000001
 *       "Logoff"="NPLogoff"
 *
 ***********************************************************************/

#include "lcredmgr.h"
#include <stdio.h>

//===[ External data ]=====================================================

//===[ Manifest constants ]================================================

//
// The authentication type is only used here in a Unicode context
//
#define MSV1_0_AUTH_TYPE      L"MSV1_0:Interactive"
#define KERB_AUTH_TYPE        L"Kerberos:Interactive"
#define WNNC_CRED_MANAGER     0xFFFF0000

#define WINDOWS_LOGON_ID      1

//===[ Type definitions ]==================================================

//===[ Function prototypes ]===============================================

BOOL WriteLogFile(LPTSTR String);

DWORD
APIENTRY
NPGetCaps (
   DWORD   ndex
   );

DWORD
APIENTRY
NPLogonNotify (
   PLUID               lpLogonId,
   LPCWSTR             lpAuthentInfoType,
   LPVOID              lpAuthentInfo,
   LPCWSTR             lpPreviousAuthentInfoType,
   LPVOID              lpPreviousAuthentInfo,
   LPWSTR              lpStationName,
   LPVOID              StationHandle,
   LPWSTR              *lpLogonScript
   );

DWORD
APIENTRY
NPPasswordChangeNotify (
   LPCWSTR             lpAuthentInfoType,
   LPVOID              lpAuthentInfo,
   LPCWSTR             lpPreviousAuthentInfoType,
   LPVOID              lpPreviousAuthentInfo,
   LPWSTR              lpStationName,
   LPVOID              StationHandle,
   DWORD               dwChangeInfo
   );

//===[ Global variables ]==================================================

HMODULE        g_hModule;
HANDLE         g_hModuleMutex = NULL;
BOOLEAN        g_bLibraryLoaded = FALSE;

HINSTANCE      g_hCASALibrary;

PSETCREDENTIAL pCASASetCredential;
POPENCACHE     pCASAOpenCache;
PCLOSECACHE    pCASACloseCache;

#ifdef _DEBUG

void
DebugPrint
(
   char *format,
   ...
)
{
   va_list     parameters;   /* Pointer to parameters on the stack  */
   char        newString[600] = {"=> LCredMgr: "};
   /*
      Get a pointer to the parameters
   */
   va_start(parameters, format);
   vsprintf( &newString[13], format, parameters );
   OutputDebugStringA(newString);

}

#endif

//++=======================================================================
BOOL
APIENTRY
DllMain (
   HANDLE hInst,
   DWORD dwReason,
   LPVOID lpReserved
   )
//=======================================================================--
{
   
#ifdef _DEBUG
   DebugPrint("DllMain called.\n");
#endif

   switch (dwReason)
   {
      case DLL_PROCESS_ATTACH:

         DisableThreadLibraryCalls((HINSTANCE)hInst);

         g_hModule = (HMODULE)hInst;

         g_hModuleMutex = CreateMutex(NULL, FALSE, NULL);

         if (g_hModuleMutex == NULL)
         {
            return(FALSE);
         }

         InitLoginExtension();

         //
         // Always succeed, this allows us to be configured with regsvr32.exe
         //

         break;

      case DLL_THREAD_ATTACH:
      case DLL_THREAD_DETACH:
         break;

      case DLL_PROCESS_DETACH:
         CloseHandle(g_hModuleMutex);
         break;
   }

   return TRUE;
}

//++=======================================================================
DWORD
WINAPI
NPGetCaps (
   DWORD nIndex
   )
//
//   PURPOSE:  This entry point is called to query the provider.  The parameter
//             is an index representing the query.  For a credential manager
//             only the following index values apply:
//
//             WNNC_SPEC_VERSION - What version of the provider specification
//                was used in developing this provider?  The return value is
//                the version number.
//
//             WNNC_DRIVER_VERSION - The version of the provider.
//
//             WNNC_START - Will the provider start? When? The return values
//                are:
//
//                    - 0 : Only return this if the provider will *not* start.
//                    - Estimated Start time in milliseconds : This is how
//                        long the provider is expected to take to start.
//                    - 0xFFFFFFFF : Time to start is unknown.
//                    - 1 : Provider is already started.
//
//             A return value of 0 in other cases indicates that the query
//             is not supported.
//=======================================================================--
{
   DWORD dwRes;

#ifdef _DEBUG
   DebugPrint("NPGetCaps called with nIndex %u.\n", nIndex);
#endif

   switch (nIndex)
   {
      case WNNC_NET_TYPE:
         dwRes = WNNC_CRED_MANAGER;
         break;

      case WNNC_SPEC_VERSION:
         dwRes = WNNC_SPEC_VERSION51;  // We are using version 5.1 of the spec.
         break;

      case WNNC_DRIVER_VERSION:
         dwRes = 1;  // This driver is version 1.
         break;

      case WNNC_START:
         dwRes = 1;  // We are already "started"
         break;

      default:
         dwRes = 0;  // We don't support anything else
         break;
   }

#ifdef _DEBUG
   DebugPrint("NPGetCaps returning dwRes %u.\n", dwRes);
#endif

   return dwRes;
}


//++=======================================================================
BOOLEAN
InitCASALibrary (
   )
//
//   PURPOSE:  Loads the micasa dll entry points.
//=======================================================================--
{
   BOOLEAN  bLibraryLoaded = TRUE;

   if (g_bLibraryLoaded == FALSE)
   {
      WaitForSingleObjectEx(g_hModuleMutex, INFINITE, FALSE);
      bLibraryLoaded = FALSE;

      if (g_bLibraryLoaded == FALSE)
      {
         g_hCASALibrary = LoadLibrary(L"micasa.dll");

         if (g_hCASALibrary)
         {
            pCASASetCredential = (PSETCREDENTIAL)GetProcAddress(g_hCASALibrary, "miCASASetCredential");
            pCASAOpenCache = (POPENCACHE)GetProcAddress(g_hCASALibrary, "miCASAOpenSecretStoreCache");
            pCASACloseCache = (PCLOSECACHE)GetProcAddress(g_hCASALibrary, "miCASACloseSecretStoreCache");

            bLibraryLoaded = TRUE;
            g_bLibraryLoaded = TRUE;
         }
#ifdef _DEBUG
         else
         {
            DebugPrint("LoadLibrary for micasa.dll failed. %d\n", GetLastError());
         }
#endif
      }

      ReleaseMutex(g_hModuleMutex);
   }

   return bLibraryLoaded;
}


//++=======================================================================
DWORD
WINAPI
NPLogonNotify (
   PLUID               lpLogonId,
   LPCWSTR             lpAuthentInfoType,
   LPVOID              lpAuthentInfo,
   LPCWSTR             lpPreviousAuthentInfoType,
   LPVOID              lpPreviousAuthentInfo,
   LPWSTR              lpStationName,
   LPVOID              StationHandle,
   LPWSTR              *lpLogonScript
   )
//
//   PURPOSE:  This entry point is called when a user logs on.  If the user
//             authentication fails here, the user will still be logged on
//             to the local machine.
//=======================================================================--
{
   int                        ccode;
   PMSV1_0_INTERACTIVE_LOGON  pAuthInfo;
   SSCS_BASIC_CREDENTIAL      basicCredential;
   SSCS_SECRET_ID_T           desktopCredential;
   SSCS_SECRET_ID_T           domainCredential;
   SSCS_EXT_T                 extension;
   BOOLEAN                    bLibraryLoaded;

#ifdef _DEBUG
   DebugPrint("NPLogonNotify called.\n");
#endif

   SetLastError(NO_ERROR);

   bLibraryLoaded = InitCASALibrary();

   if (bLibraryLoaded == FALSE)
   {
      SetLastError(NO_ERROR);
      return NO_ERROR;
   }

   //
   // If the primary authenticator is not MSV1_0, return success.
   // Why? Because this is the only auth info structure that we
   // understand and we don't want to interact with other types.
   //

   if (!lstrcmpiW (MSV1_0_AUTH_TYPE, lpAuthentInfoType))
   {
#ifdef _DEBUG
      DebugPrint("NPLogonNotify auth type is MSV1_0_AUTH_TYPE.\n");
#endif
   }
   else if (!lstrcmpiW (KERB_AUTH_TYPE, lpAuthentInfoType))
   {
#ifdef _DEBUG
      DebugPrint("NPLogonNotify auth type is KERB_AUTH_TYPE.\n");
#endif
   }
   else
   {
#ifdef _DEBUG
      DebugPrint("NPLogonNotify auth type is UNSUPPORTED.\n");
#endif

      SetLastError(NO_ERROR);
      return NO_ERROR;
   }

   if ((wcscmp(L"SvcCtl", lpStationName)) == 0)
   {
#ifdef _DEBUG
      DebugPrint("NPLogonNotify stationname indicates a service has logged on.\n");
#endif
      return NO_ERROR;
   }

   //
   // Do something with the authentication information
   //

   pAuthInfo = (PMSV1_0_INTERACTIVE_LOGON) lpAuthentInfo;

   basicCredential.unLen = WideCharToMultiByte(
                              CP_UTF8,
                              0,
                              pAuthInfo->UserName.Buffer,
                              -1,
                              (LPSTR)&basicCredential.username,
                              NSSCS_MAX_USERID_LEN,
                              NULL,
                              NULL);

   if (basicCredential.unLen)
   {
//      basicCredential.unLen++;

      basicCredential.pwordLen = WideCharToMultiByte(
                                 CP_UTF8,
                                 0,
                                 pAuthInfo->Password.Buffer,
                                 -1,
                                 (LPSTR)&basicCredential.password,
                                 NSSCS_MAX_PWORD_LEN,
                                 NULL,
                                 NULL);

      if (basicCredential.pwordLen)
      {
//         basicCredential.pwordLen++;

         strcpy((char *)&desktopCredential.id, "Desktop");
         desktopCredential.len = (long)strlen((char *)&desktopCredential.id) + 1;

         basicCredential.unFlags = USERNAME_TYPE_CN_F;

         extension.extID = WINDOWS_LOGON_ID;
         extension.version = 0x00010000;  // 1.0.0
         extension.ext = (void *)lpLogonId;

         ccode = (*pCASASetCredential)(
                  0,
                  &desktopCredential,
                  NULL,
                  SSCS_CRED_TYPE_BASIC_F,
                  &basicCredential,
                  &extension);

#ifdef _DEBUG
         if (!ccode)
         {
            DebugPrint("NSSCSSetCredential successful!.\n");
         }
         else
         {
            DebugPrint("NSSCSSetCredential failed 0x%X\n", ccode);
         }
#endif

         if (pAuthInfo->LogonDomainName.Length != 0)
         {
            domainCredential.len = WideCharToMultiByte(
                                    CP_UTF8,
                                    0,
                                    pAuthInfo->LogonDomainName.Buffer,
                                    -1,
                                    (LPSTR)&domainCredential.id,
                                    NSSCS_MAX_SECRET_ID_LEN,
                                    NULL,
                                    NULL);

            if (domainCredential.len)
            {
//               domainCredential.len++;

#ifdef _DEBUG
               DebugPrint("Domain exists - [%s], length %d\n", domainCredential.id, domainCredential.len);
#endif

               ccode = (*pCASASetCredential)(
                        0,
                        &desktopCredential,
                        &domainCredential,
                        SSCS_CRED_TYPE_BASIC_F,
                        &basicCredential,
                        &extension);

#ifdef _DEBUG
               if (!ccode)
               {
                  DebugPrint("NSSCSSetCredential successful!.\n");
               }
               else
               {
                  DebugPrint("NSSCSSetCredential failed 0x%X\n", ccode);
               }
#endif
            }

         }
      }
#ifdef _DEBUG
      else
      {
         DebugPrint("WideCharToMultiByte for password failed.\n");
      }
#endif
   }
#ifdef _DEBUG
   else
   {
      DebugPrint("WideCharToMultiByte for user name failed.\n");
   }
#endif

   return NO_ERROR;
}

//++=======================================================================
DWORD
WINAPI
NPPasswordChangeNotify (
   LPCWSTR             lpAuthentInfoType,
   LPVOID              lpAuthentInfo,
   LPCWSTR             lpPreviousAuthentInfoType,
   LPVOID              lpPreviousAuthentInfo,
   LPWSTR              lpStationName,
   LPVOID              StationHandle,
   DWORD               dwChangeInfo
   )
//
//   PURPOSE:  This function is used to notify a credential manager provider
//             of a password change (or, more accurately, an authentication
//             information change) for an account.
//=======================================================================--
{
//   return NO_ERROR;
#ifdef _DEBUG
   DebugPrint("NPPasswordChangeNotify called.\n");
#endif
   return WN_NOT_SUPPORTED;
}

//++=======================================================================
VOID
WINAPI
NPLogoff(
   PWLX_NOTIFICATION_INFO pInfo
   )
//=======================================================================--
{
   DWORD                dwSize;
   TOKEN_STATISTICS     tokenStats;
   SSCS_SECRETSTORE_T   ssId;
   HANDLE               context;
   BOOLEAN              bLibraryLoaded;

#ifdef _DEBUG
   DebugPrint("NPLogoff called.\n");
#endif

   dwSize = sizeof(tokenStats);

   if (GetTokenInformation(
         pInfo->hToken,
         TokenStatistics,
         &tokenStats,
         dwSize,
         &dwSize))
   {
#ifdef _DEBUG
      DebugPrint("GetTokenInformation for statistics returned LowPart 0x%X HighPart 0x%X\n", tokenStats.AuthenticationId.LowPart, tokenStats.AuthenticationId.HighPart);
#endif


   }
#ifdef _DEBUG
   else
   {
      DebugPrint("GetTokenInformation for TokenStatistics failed %u\n", GetLastError());
   }
#endif

   bLibraryLoaded = TRUE;
   WaitForSingleObjectEx(g_hModuleMutex, INFINITE, FALSE);

   if (g_bLibraryLoaded == FALSE)
   {
#ifdef _DEBUG
      DebugPrint("Library not loaded in NPLogoff.\n");
#endif
      bLibraryLoaded = FALSE;
   }

   ReleaseMutex(g_hModuleMutex);

   if (bLibraryLoaded == FALSE)
   {
      return;
   }

   ssId.version = NSSCS_VERSION_NUMBER;
   strcpy((char *)ssId.ssName, (char *)SSCS_DEFAULT_SECRETSTORE_ID);
  
   context = (*pCASAOpenCache)(
               &ssId,
               0,
               NULL);

   if (context)
   {
      //
      // the SSFLAG_DESTROY_SESSION_F closes the session for this user
      //

      (*pCASACloseCache)(
         context,
         SSFLAG_DESTROY_SESSION_F,
         NULL);
   }

   return;
}

//++=======================================================================
DWORD
AddSubString(
   HKEY hKey
)
//=======================================================================--
{
   LONG     rc;
   CHAR     szValue[256],
            szNewValue[256];
   DWORD    valueSize;


   valueSize = sizeof(szValue);

   rc = RegQueryValueExA(
         hKey,
         "ProviderOrder",
         0,
         NULL,
         (PUCHAR)szValue,
         &valueSize);

   if (rc == S_OK)
   {
      strcpy (szNewValue, "LCredMgr,");
      strcat (szNewValue, szValue);

      rc = RegSetValueExA(
            hKey,
            "ProviderOrder",
            0,
            REG_SZ,
            (PUCHAR)szNewValue,
            (DWORD) strlen(szNewValue));
   }

   return rc;
}

//++=======================================================================
DWORD
RemoveSubString(
   HKEY hKey
)
//=======================================================================--
{
   LONG     rc;
   CHAR     szValue[256],
            szNewValue[256];
   PCHAR    subString, startString, endString;
   DWORD    valueSize;


   valueSize = sizeof(szValue);

   rc = RegQueryValueExA(
         hKey,
         "ProviderOrder",
         0,
         NULL,
         (PUCHAR)szValue,
         &valueSize);

   if (rc == S_OK)
   {
      //
      // Remove any leading whitespace
      //

      subString = szValue;

      while (*subString != NULL && (*subString == ',' || *subString == ' '))
      {
         subString++;
      }

      startString = subString;

      //
      // Check to see if lcredmgr is in the string
      //

      if ((subString = strstr(startString, "LCredMgr")) != NULL)
      {
         if (subString == startString)
         {
            while (*subString != ',' && *subString != NULL)
            {
               subString++;
            }

            while ((*subString == ',' || *subString == ' ') && *subString != NULL)
            {
               subString++;
            }

            startString = subString;
         }
         else
         {
            endString = subString + 8;

            //
            // Back up past any whitespace or comma's.
            //

            for (subString--;;subString--)
            {
               if (*subString != ' ' && *subString != ',')
               {
                  *(subString + 1) = '\0';
                  break;
               }
            }

            //
            // Get past any comma's
            //

            for (;;endString++)
            {
               if (*endString == NULL)
               {
                  break;
               }

               if ((*endString != ',' && *endString != ' '))
               {
                  //
                  // concat the two strings without lcredmgr
                  //

                  strcpy (szNewValue, startString);
                  strcat (szNewValue, endString);
                  startString = szNewValue;

                  break;
               }
            }
         }

         rc = RegSetValueExA(
               hKey,
               "ProviderOrder",
               0,
               REG_SZ,
               (PUCHAR)startString,
               (DWORD) strlen(startString));
      }
   }

   return rc;
}

//++=======================================================================
HRESULT
DllInstall(
   BOOL bInstall,
   LPCWSTR pszCmdLine
)
//=======================================================================--
{
   HKEY     hKey;
   LONG     rc;
   CHAR     szModule[256];
   DWORD    value, valueSize;

#ifdef _DEBUG
   DebugPrint("DllInstall called\n");
   OutputDebugStringW(pszCmdLine);
#endif


   if (bInstall == TRUE)
   {
      rc = RegCreateKeyExA(
            HKEY_LOCAL_MACHINE,
            "System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
            0,
            NULL,
            REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS,
            NULL,
            &hKey,
            NULL);

      if (rc == S_OK)
      {
         rc = RemoveSubString(hKey);

         if (rc == S_OK)
         {
            rc = AddSubString(hKey);
         }

         RegCloseKey(hKey);
      }

      //
      // Set up our service key
      //

      if (rc == S_OK)
      {
         rc = RegCreateKeyExA(
               HKEY_LOCAL_MACHINE,
               "System\\CurrentControlSet\\Services\\LCredMgr\\networkprovider",
               0,
               NULL,
               REG_OPTION_NON_VOLATILE,
               KEY_ALL_ACCESS,
               NULL,
               &hKey,
               NULL);

         if (rc == S_OK)
         {
            value = 2;

            rc = RegSetValueExA(
                  hKey,
                  "Class",
                  0,
                  REG_DWORD,
                  (BYTE *)&value,
                  sizeof(value));

            if (rc != S_OK)
            {
               goto ErrorExit;
            }

            rc = RegSetValueExA(
                  hKey,
                  "Name",
                  0,
                  REG_SZ,
                  (BYTE *)"LoginCapture Credential Provider",
                  32);

            if (rc != S_OK)
            {
               goto ErrorExit;
            }

            valueSize = GetModuleFileNameA((HMODULE)g_hModule, szModule, sizeof(szModule));

            if (valueSize)
            {
               rc = RegSetValueExA(
                     hKey,
                     "ProviderPath",
                     0,
                     REG_SZ,
                     (BYTE *)szModule,
                     valueSize + 1);
            }
            else
            {
               rc = GetLastError();
            }

            RegCloseKey(hKey);
         }
      }

      if (rc == S_OK)
      {
         rc = RegCreateKeyExA(
               HKEY_LOCAL_MACHINE,
               "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\LCredMgr",
               0,
               NULL,
               REG_OPTION_NON_VOLATILE,
               KEY_ALL_ACCESS,
               NULL,
               &hKey,
               NULL);

         if (rc == S_OK)
         {
            value = 1;

            rc = RegSetValueExA(
                  hKey,
                  "Asynchronous",
                  0,
                  REG_DWORD,
                  (BYTE *)&value,
                  sizeof(value));

            if (rc != S_OK)
            {
               goto ErrorExit;
            }

            rc = RegSetValueExA(
                  hKey,
                  "DLLName",
                  0,
                  REG_SZ,
                  (BYTE *)szModule,
                  valueSize + 1);

            if (rc != S_OK)
            {
               goto ErrorExit;
            }

            rc = RegSetValueExA(
                  hKey,
                  "Impersonate",
                  0,
                  REG_DWORD,
                  (BYTE *)&value,
                  sizeof(value));

            if (rc != S_OK)
            {
               goto ErrorExit;
            }

            rc = RegSetValueExA(
                  hKey,
                  "Logoff",
                  0,
                  REG_SZ,
                  (BYTE *)"NPLogoff",
                  9);

            RegCloseKey(hKey);
         }
      }

      if (rc == S_OK)
      {
         rc = RegCreateKeyExA(
               HKEY_LOCAL_MACHINE,
               "SOFTWARE\\Novell\\Graphical Login\\NWLGE\\LCredMgr",
               0,
               NULL,
               REG_OPTION_NON_VOLATILE,
               KEY_ALL_ACCESS,
               NULL,
               &hKey,
               NULL);

         if (rc == S_OK)
         {
            rc = RegSetValueExA(
                  hKey,
                  "LoginExtName",
                  0,
                  REG_SZ,
                  (BYTE *)szModule,
                  valueSize + 1);

            if (rc != S_OK)
            {
               goto ErrorExit;
            }

            rc = RegSetValueExA(
                  hKey,
                  "LoginExtDesc",
                  0,
                  REG_SZ,
                  (BYTE *)"CASA Login Extension",
                  20);

            if (rc != S_OK)
            {
               goto ErrorExit;
            }

            value = 0x00008002;

            rc = RegSetValueExA(
                  hKey,
                  "LoginExtType",
                  0,
                  REG_DWORD,
                  (BYTE *)&value,
                  sizeof(value));

ErrorExit:
            RegCloseKey(hKey);
         }
      }
   }
   else
   {
      rc = RegCreateKeyExA(
            HKEY_LOCAL_MACHINE,
            "System\\CurrentControlSet\\Control\\NetworkProvider\\Order",
            0,
            NULL,
            REG_OPTION_NON_VOLATILE,
            KEY_ALL_ACCESS,
            NULL,
            &hKey,
            NULL);

      if (rc == S_OK)
      {
         RemoveSubString(hKey);

         RegCloseKey(hKey);
      }

      //
      // Delete our service key
      //

      RegDeleteKeyA(
         HKEY_LOCAL_MACHINE,
         "System\\CurrentControlSet\\Services\\LCredMgr\\Enum");

      RegDeleteKeyA(
         HKEY_LOCAL_MACHINE,
         "System\\CurrentControlSet\\Services\\LCredMgr\\networkprovider");

      RegDeleteKeyA(
         HKEY_LOCAL_MACHINE,
         "System\\CurrentControlSet\\Services\\LCredMgr");

      RegDeleteKeyA(
         HKEY_LOCAL_MACHINE,
         "SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion\\Winlogon\\Notify\\LCredMgr");

      RegDeleteKeyA(
         HKEY_LOCAL_MACHINE,
         "SOFTWARE\\Novell\\Graphical Login\\NWLGE\\LCredMgr");
   }

#ifdef _DEBUG
   if (rc == S_OK)
   {
      DebugPrint("DllInstall successful.\n");
   }
   else
   {
      DebugPrint("DllInstall failed.\n");
   }
#endif

   return rc;
}

//++=======================================================================
STDAPI
DllRegisterServer(
   void
   )
//=======================================================================--
{
#ifdef _DEBUG
   DebugPrint("DllRegisterServer called\n");
#endif

   return S_OK;
}

//++=======================================================================
STDAPI
DllUnregisterServer(
   void
   )
//=======================================================================--
{
#ifdef _DEBUG
   DebugPrint("DllUnregisterServer called\n");
#endif

   return S_OK;
}

//=========================================================================
//=========================================================================