AcceptSecurityContext завершается неудачно, когда приложение работает как служба

у меня есть простой HTTP-сервер, который аутентифицирует клиентов с помощью протокола переговоров. Он использует вызовы SSPI для получения учетных данных сервера и установления контекста безопасности. Сервер находится в домене и работает от имени пользователя домена. Все работает нормально, и я получаю ответ HTTP 200, если я запускаю сервер в консольном режиме. Однако, когда я запускаю его как службу, я получаю ошибку SEC_E_INVALID_HANDLE. Вот что происходит, когда я запускаю его в консоли режим:

1.Клиент отправляет запрос HTTP Get http://localhost:8082

2.Сервер отвечает заголовком WWW-Authenticate:Negotiate.

3.Клиент отправляет заголовок авторизации и включает следующие данные:

60 73 06 06 2B 06 01 05 05 02 A0 69 30 67 A0 30  `s..+..... i0g 0
30 2E 06 0A 2B 06 01 04 01 82 37 02 02 0A 06 09  0...+....7.....
2A 86 48 82 F7 12 01 02 02 06 09 2A 86 48 86 F7  *H÷......*H÷
12 01 02 02 06 0A 2B 06 01 04 01 82 37 02 02 1E  ......+....7...
A2 33 04 31 4E 54 4C 4D 53 53 50 00 01 00 00 00  ¢3.1NTLMSSP.....
97 B2 08 E2 04 00 04 00 2D 00 00 00 05 00 05 00  ².â....-.......
28 00 00 00 06 01 B1 1D 00 00 00 0F 50 41 43 45  (.....±.....PACE
4D 42 4C 41 48                                   MBLAH             

4.Сервер отвечает с ошибкой HTTP 401 и согласовывает заголовок, предлагающий продолжить:

A1 81 CE 30 81 CB A0 03 0A 01 01 A1 0C 06 0A 2B  ¡Î0Ë ....¡...+
06 01 04 01 82 37 02 02 0A A2 81 B5 04 81 B2 4E  ....7...¢µ.²N
54 4C 4D 53 53 50 00 02 00 00 00 08 00 08 00 38  TLMSSP.........8
00 00 00 15 C2 89 E2 B0 3B BE 20 45 33 FD 92 80  ....Ââ°;¾ E3ý
04 E7 01 00 00 00 00 72 00 72 00 40 00 00 00 06  .ç.....r.r.@....
01 B1 1D 00 00 00 0F 42 00 4C 00 41 00 48 00 02  .±.....B.L.A.H..
00 08 00 42 00 4C 00 41 00 48 00 01 00 0A 00 50  ...B.L.A.H.....P
00 41 00 43 00 45 00 4D 00 04 00 10 00 62 00 6C  .A.C.E.M.....b.l
00 61 00 68 00 2E 00 63 00 6F 00 6D 00 03 00 1C  .a.h...c.o.m....
00 50 00 61 00 63 00 65 00 6D 00 2E 00 62 00 6C  .P.a.c.e.m...b.l
00 61 00 68 00 2E 00 63 00 6F 00 6D 00 05 00 10  .a.h...c.o.m....
00 62 00 6C 00 61 00 68 00 2E 00 63 00 6F 00 6D  .b.l.a.h...c.o.m
00 07 00 08 00 5D B3 C5 9A 0F 17 D1 01 00 00 00  .....]³Å..Ñ....
00                                               .             

5.Клиент отправляет заголовок авторизации:

A1 77 30 75 A0 03 0A 01 01 A2 5A 04 58 4E 54 4C  ¡w0u ....¢Z.XNTL
4D 53 53 50 00 03 00 00 00 00 00 00 00 58 00 00  MSSP.........X..
00 00 00 00 00 58 00 00 00 00 00 00 00 58 00 00  .....X.......X..
00 00 00 00 00 58 00 00 00 00 00 00 00 58 00 00  .....X.......X..
00 00 00 00 00 58 00 00 00 15 C2 88 E2 06 01 B1  .....X....Ââ..±
1D 00 00 00 0F C0 BD 0C 5B F5 F9 35 FE 78 6D 08  .....À½.[õù5þxm.
BF 7B D9 CC E3 A3 12 04 10 01 00 00 00 F5 17 A7  ¿{ÙÌã£.......õ.§
50 2D 22 9A 84 00 00 00 00                       P-"....     

6.Сервер отвечает HTTP 200 и заголовок переговоров:

A1 1B 30 19 A0 03 0A 01 00 A3 12 04 10 01 00 00  ¡.0. ....£......
00 43 87 E0 88 C1 36 E3 A9 00 00 00 00           .CàÁ6ã©....   

теперь, если я запущу приложение как службу, я получу почти идентичные ответы, но AcceptSecurityContext потерпит неудачу на шаге #6 и вернет ошибку SEC_E_INVALID_HANDLE. Интересно, почему это произойдет, если я запущу то же приложение и укажу того же пользователя в качестве идентификатора входа в систему? Может быть, это как-то связано с изоляцией сеанса 0? Также есть способ устранения его лучше, я не вижу никаких сообщений об ошибках в средстве просмотра событий и недопустимый дескриптор ошибка не говорит о том, чего не хватает.

вот код сервера для аутентификации:

public static WinAuthResult Authenticate(string clientId, byte[] clientTokenBytes, string securityPackage, ILogger logger)
{
    if (clientTokenBytes == null || clientTokenBytes.Length == 0)
    {
        ClearContext(clientId);
        throw new Win32Exception(Secur32.SEC_E_INVALID_TOKEN);
    }

    var serverCredExpiry = new Secur32.SECURITY_INTEGER();
    var serverCredHandle = new Secur32.SecHandle();
    var acquireResult = Secur32.AcquireCredentialsHandle(null, securityPackage, Secur32.SECPKG_CRED_INBOUND, IntPtr.Zero, IntPtr.Zero, 0, IntPtr.Zero, out serverCredHandle, out serverCredExpiry);
    if (acquireResult != Secur32.SEC_E_OK)
        throw new Win32Exception(acquireResult);

    var oldContextExists = contexts.ContainsKey(clientId);
    var oldContextHandle = GetContextHandle(clientId);
    var newContextHandle = new Secur32.SecHandle();
    var clientToken = new Secur32.SecBufferDesc(clientTokenBytes);
    var outputToken = new Secur32.SecBufferDesc(61440);
    var contextAttributes = (uint)0;
    var outputCresExpiry = new Secur32.SECURITY_INTEGER();

    int acceptResult;
    if (!oldContextExists)
    {
        acceptResult = Secur32.AcceptSecurityContext(
            ref serverCredHandle,
            IntPtr.Zero,
            ref clientToken,
            0,
            Secur32.SECURITY_NATIVE_DREP,
            ref newContextHandle,
            ref outputToken,
            out contextAttributes,
            out outputCresExpiry);
    }
    else
    {
        acceptResult = Secur32.AcceptSecurityContext(
            ref serverCredHandle,
            ref oldContextHandle,
            ref clientToken,
            0,
            Secur32.SECURITY_NATIVE_DREP,
            ref newContextHandle,
            ref outputToken,
            out contextAttributes,
            out outputCresExpiry);
    }

    if (acceptResult == Secur32.SEC_E_OK)
    {
        ClearContext(clientId);
        return new WinAuthResult(false, outputToken.GetSecBufferByteArray());
    }
    else if (acceptResult == Secur32.SEC_I_CONTINUE_NEEDED)
    {
        contexts[clientId] = newContextHandle;
        return new WinAuthResult(true, outputToken.GetSecBufferByteArray());
    }
    else
    {
        ClearContext(clientId);
        throw new Win32Exception(acceptResult);
    }
}

в обоих случаях я пытаюсь получить доступ к веб-странице с той же машины, на которой работает сервер, и с тем же пользователем домена. Также я использую того же пользователя домена для запуска консольного приложения и службы windows. Проблема не воспроизводится в Windows Server 2003, что заставляет меня думать, что это связано с новыми функциями безопасности.

1 ответов


прошло некоторое время, но я думаю, что видел подобные проблемы, когда Загрузить Профиль Пользователя был установлен до false в расширенных настройках пула приложений. Этот параметр является новым для IIS 7.0. В документации говорится, что false значение соответствует поведению Windows Server 2003, но я помню, что не загрузка профиля каким-то образом мешает подсистеме SSPI. И вы правы, там очень мало сообщений об ошибках оттуда, я должен был прыгать через некоторые обручи, чтобы дразнить ответ из него.

enter image description here

обновление

эти функции в конечном счете полагаются на реализацию клиента Kerberos, большая часть которого находится в lsass.процесс exe. Вот хорошая ссылка на устранение неполадок всей подсистемы: http://blogs.msdn.com/b/canberrapfe/archive/2012/01/02/kerberos-troubleshooting.aspx

кроме того, я помню, что однажды у клиента были проблемы с аутентификацией, которые мы в конечном итоге проследили до некоторого несоответствия протокола между клиентом, работающим на сервере 2008 (или что-то в этом роде, важный факт-версия была выше 2003), подключаясь к вторичному контроллеру домена, работающему на сервере 2003. Не проследил его дальше, клиент просто обновил DC.

ОКОНЧАТЕЛЬНОЕ ОБНОВЛЕНИЕ

хорошо, я смог воспроизвести проблему, и я действительно смог заставить ее работать. Ваш Authenticate(string clientId, byte[] clientTokenBytes, string securityPackage, ILogger logger) метод вызывается, по крайней мере, дважды за первый вызов AcceptSecurityContext возвращение SEC_I_CONTINUE_NEEDED. Каждый раз Authenticate получает новый дескриптор учетных данных, вызывая