15 мая 2023 года "Исходники.РУ" отмечают своё 23-летие!
Поздравляем всех причастных и неравнодушных с этим событием!
И огромное спасибо всем, кто был и остаётся с нами все эти годы!

Главная Форум Журнал Wiki DRKB Discuz!ML Помощь проекту


Как программно осуществить вход в сеть в WinNT и Win95

Иногда бывает необходимо, чтобы приложение проверяло имя пользователя и пароль. Существует пара способов, чтобы проделать эту операцию, в зависимости от того, на какой платформе запущено приложение: Windows 95 или Windows NT.

В данной статье описаны все общие способы проверки прав пользователя, а так же специальные требования для каждого из методов.

API функция LogonUser доступна и документирована начиная с Windows NT 3.51, и обычно используется для проверки прав пользователя. К сожалению, есть некоторые ограничения на использование LogonUser. Первое и самое большое ограничение, это то, что процесс, вызывающий LogonUser должен иметь привелегию SE_TCB_NAME. SE_TCB_NAME очень серьёзная привелегия и не может предоставляться любому пользователю для управлением процессом авторизации. Рекомендуется вызывать LogonUser из сервиса, запущенного аккаунтом системы, так как системный аккаунт изначально имеет привелегию SE_TCB_NAME.

Другая проблема LogonUser в том, эта API не включена в Windows 95.

Однако, можно использовать Security Support Provider Interface (SSPI) чтобы осуществить вход в сеть с поддержкой авторизации. У этого метода есть преимущество, так как он не требует никаких специальных привелегий, а так же работает в Windows 95. Конечный результат использования сервисов SSPI для проверки прав пользователя идентичен вызову LogonUser с типом входа LOGON32_LOGON_NETWORK. Однако, при данно типе входа в сеть Вы не сможете получить доступ к удалённым сетевым ресурсам. Если Ваше приложение будет вызывать LogonUser с типом входа LOGON32_LOGON_INTERACTIVE, то нельзя будет работать с Windows NT, поэтому в данном случае применение SSPI как альтернативы для входа/авторизации не подойдёт.

Приведённый ниже пример, показывает, как вызвать сервис SSPI для проверки прав пользователя. Это всего лишь модификация примера SOCKAUTH из Platform SDK, который использует сервис SSPI. Для компиляции нижеприведённого кода, необходимы два модуля из этого примера.

Чтобы использовать этот пример в Windows 95, Вам необходимо загрузить пакет SSP для Windows 95. В Windows 95 пакет SSP называется Secur32.dll, в отличиет от Windows NT, где он называется Security.dll . Для этого необходимо в SECURITY.C изменить значение константы NT_DLL_NAME.

Чтобы использовать данный метод в Windows 95, необходимо разрешить сервис безопасности NTLM (Control Panel -> Network -> Access Control, а затем указав User-level (на уровне пользователей)).

Пример кода

   /*++

   Module Name:

       SSPLogon.c

   Abstract:

      This module implements the network logon type by
      interfacing with the Windows NT Lan Man Security Support
      Provider (NTLMSSP) for the purpose of validating
      the provided users credentials.


   Автор:

      David Mowers (DaveMo) January 14, 1998

   Необходимы следующие модули из примера SockAuth:

      security.c (modify according to comment below)
      collect.c

   Revision History:

   --*/ 

   #define SECURITY_WIN32

   #include <windows.h>
   #include <sspi.h>

   // 
   // Небольшое изменение в GenClientContext, чтобы можно было
   // передавать пользовательские данные авторизации.
   // 
   BOOL GenClientContext (
      DWORD dwKey,
      SEC_WINNT_AUTH_IDENTITY *pAuthIdentity,
      BYTE *pIn,
      DWORD cbIn,
      BYTE *pOut,
      DWORD *pcbOut,
      BOOL *pfDone);

   /*

      В security.c, для функции GenClientContext,
      сделайте следующие изменения:

      ss = g_pFuncs->AcquireCredentialsHandle (
         NULL,   // principal
         PACKAGE_NAME,
         SECPKG_CRED_OUTBOUND,
         NULL,   // LOGON id
         pAuthIdentity,   // auth data
         NULL,   // get key fn
         NULL,   // get key arg
         &pAS->_hcred,
         &Lifetime
         );
   */ 


   static PBYTE g_pClientBuf = NULL;
   static PBYTE g_pServerBuf = NULL;
   static DWORD g_cbMaxMessage = 0;

   BOOL
   SSPLogonUser(
      LPTSTR DomainName,
      LPTSTR UserName,
      LPTSTR Password
      )
   {

      BOOL done = FALSE;
      DWORD cbOut, cbIn;
      char szUser[80];
      DWORD cbUser = 80;
      SEC_WINNT_AUTH_IDENTITY AuthIdentity;

      if(!InitSession(0))
      {
         return(FALSE);
      }

      if(!InitSession(1))
      {
         return(FALSE);
      }

      if (!InitPackage (&g_cbMaxMessage))
      {
         return(FALSE);
      }

      g_pClientBuf = (PBYTE) malloc(g_cbMaxMessage);
      g_pServerBuf = (PBYTE) malloc(g_cbMaxMessage);

      ZeroMemory( &AuthIdentity, sizeof(AuthIdentity) );

      if ( DomainName != NULL )
      {
         AuthIdentity.Domain = DomainName;
         AuthIdentity.DomainLength = lstrlen(DomainName);
      }

      if ( UserName != NULL )
      {
         AuthIdentity.User = UserName;
         AuthIdentity.UserLength = lstrlen(UserName);
      }

      if ( Password != NULL )
      {
         AuthIdentity.Password = Password;
         AuthIdentity.PasswordLength = lstrlen(Password);
      }

   #ifdef UNICODE
       AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_UNICODE;
   #else
      AuthIdentity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
   #endif

      // 
      // Подготовка клиентского сообщения (negotiate).
      // 
      cbOut = g_cbMaxMessage;
      if (!GenClientContext (
         0,
         &AuthIdentity,
         NULL,
         0,
         g_pClientBuf,
         &cbOut,
         &done))
      {
         return(FALSE);
      }

      cbIn = cbOut;
      // 
      // Подготовка сообщения сервера (challenge).
      // 
      cbOut = g_cbMaxMessage;
      if (!GenServerContext (
         1,
         g_pClientBuf,
         cbIn,
         g_pServerBuf,
         &cbOut,
         &done))
      {
         // 
         // Наиболее вероятный отказ: AcceptServerContext с сообщением
         // SEC_E_LOGON_DENIED в случе неправильного имени или пароля
         // 
         // Неожиданный Результат: Вход будет удачным, если имя
         // неправильно, а гостевой аккаунт присутствует в указанно
         // домене.
         // 
         return(FALSE);
      }

      cbIn = cbOut;
      // 
      // Подготовка клиентского сообщения (authenticate).
      // 
      cbOut = g_cbMaxMessage;
      if (!GenClientContext (
         0,
         &AuthIdentity,
         g_pServerBuf,
         cbIn,
         g_pClientBuf,
         &cbOut,
         &done))
      {
         return(FALSE);
      }

      cbIn = cbOut;
      // 
      // Подготовка сообщения сервера (authentication).
      // 
      cbOut = g_cbMaxMessage;
      if (!GenServerContext (
         1,
         g_pClientBuf,
         cbIn,
         g_pServerBuf,
         &cbOut,
         &done))
      {
         return(FALSE);
      }

      TermSession(0);
      TermSession(1);

      TermPackage();

      free(g_pClientBuf);
      free(g_pServerBuf);

      return(TRUE);
   }

   int main( int argc, char *argv[] )
   {

      if(argc<4)
      {
         printf(
            "Usage: %s <domain> <user> <password>\n",
            argv[0]);
         exit(0);
      }

      if(SSPLogonUser(
         argv[1],
         argv[2],
         argv[3]))
      {
         printf("SSP Logon Succeeded!\n");
         exit(1);
      }
      else
      {
         printf("SSP Logon Failed!\n");
         exit(0);
      }

   }