InitializeSecurityContextW 在第二次调用后返回 SEC_E_INVALID_HANDLE

问题描述 投票:0回答:0

我正在尝试使用 Winsock 内核和 Schannel 在我的内核模式应用程序中实现安全套接字。我使用this代码作为建立安全连接的参考。但是,我遇到了一个奇怪的问题,即第二次调用

InitializeSecurityContextW
失败并出现错误
SEC_E_INVALID_HANDLE
。这对我来说没有意义,因为第一次调用返回
SEC_I_CONTINUE_NEEDED
被认为是成功的。根据我对文档的理解,现在应该保证
phNewContext
参数返回一个部分形成的上下文的句柄,但实际上这个句柄在再次传递给
InitializeSecurityContextW
时会产生提到的错误代码(在
phContext 
参数),更糟糕的是,在将其传递给时会导致 BSOD
DeleteSecurityContext
。文档没有提到这种行为,这让我想知道这是否是 API 的错误? 我当前的代码如下所示(我添加了注释以指示错误发生的位置):

NTSTATUS SecureSocketConnect(
    IN SOCKET_SESSION_HANDLE handle,
    IN PSOCKADDR remoteAddr,
    OUT PSECURE_SOCK_CONNECTION_HANDLE outHandle
)
{
    NTSTATUS retVal;
    SECURITY_STATUS credStatus = E_FAIL, ctxStatus;
    PSECURE_SOCKET_CONNECTION con = NULL;
    SECURITY_STRING packageName;
    SCHANNEL_CRED creds = { 0 };
    BOOLEAN destroyCtx = FALSE, isFirst = TRUE;
    DWORD flags, downloaded = 0;

    SecBuffer inBuf[2] = { 0 }, outBuf[1] = { 0 };
    SecBufferDesc inDesc, outDesc;

    //1. Allocate and init. structures

    con = AllocUserMemory(sizeof(SECURE_SOCKET_CONNECTION));

    if (!con)
    {
        retVal = STATUS_NO_MEMORY;
        goto Done;
    }

    creds.dwVersion = SCHANNEL_CRED_VERSION;
    creds.grbitEnabledProtocols = SP_PROT_TLS1_2;
    creds.dwFlags = SCH_USE_STRONG_CRYPTO         
        | SCH_CRED_MANUAL_CRED_VALIDATION
        | SCH_CRED_NO_DEFAULT_CREDS;

    flags = ISC_REQ_USE_SUPPLIED_CREDS | 
        ISC_REQ_ALLOCATE_MEMORY |
        ISC_REQ_CONFIDENTIALITY | 
        ISC_REQ_REPLAY_DETECT | 
        ISC_REQ_SEQUENCE_DETECT | 
        ISC_REQ_STREAM;

    RtlInitUnicodeString(
        &packageName, 
        L"Microsoft Unified Security Protocol Provider"
    );

    //2. Get pointer to security function table

    con->SecureFuncs = InitSecurityInterfaceW();

    if (!con->SecureFuncs)
    {
        retVal = STATUS_UNSATISFIED_DEPENDENCIES;
        goto Done;
    }

    //3. Acquire credentials handle

    credStatus = con->SecureFuncs->AcquireCredentialsHandleW(
        NULL,
        &packageName,
        SECPKG_CRED_OUTBOUND,
        NULL,
        &creds,
        NULL,
        NULL,
        &con->CredHandle,
        NULL
    );

    if (credStatus != SEC_E_OK)
    {
        retVal = STATUS_UNSUCCESSFUL;
        goto Done;
    }

    //4. Try to connect to remote address

    retVal = SocketConnect(
        handle,
        SOCK_STREAM,
        IPPROTO_TCP,
        remoteAddr,
        &con->SocketConnection
    );

    if (!NT_SUCCESS(retVal))
        goto Done;
    
    //5. Establish secure connection using InitializeSecurityContextW
    
    for(retVal = STATUS_PENDING; retVal == STATUS_PENDING; )
    {
        inBuf[0].BufferType = SECBUFFER_TOKEN;
        inBuf[0].pvBuffer = con->Incoming;
        inBuf[0].cbBuffer = con->Received;

        inBuf[1].BufferType = SECBUFFER_EMPTY;
        inBuf[1].pvBuffer = NULL;
        inBuf[1].cbBuffer = 0;

        outBuf[0].BufferType = SECBUFFER_TOKEN;
        outBuf[0].pvBuffer = NULL;
        outBuf[0].cbBuffer = 0;

        inDesc.ulVersion = SECBUFFER_VERSION;
        inDesc.pBuffers = inBuf;
        inDesc.cBuffers = 2;

        outDesc.ulVersion = SECBUFFER_VERSION;
        outDesc.cBuffers = 1;
        outDesc.pBuffers = outBuf;

        if (isFirst)
        {
            isFirst = FALSE;

            //This (first) call succeeds with SEC_I_CONTINUE_NEEDED
            ctxStatus = con->SecureFuncs->InitializeSecurityContextW(
                &con->CredHandle,
                NULL,
                NULL,
                flags,
                0,
                0,
                NULL,
                0,
                &con->ContextHandle,
                &outDesc,
                &flags,
                NULL
            );

            if (ctxStatus != SEC_I_CONTINUE_NEEDED)
            {
                retVal = STATUS_UNSUCCESSFUL;
                continue;
            }
            else
            {
                destroyCtx = TRUE;
            }
        }
        else
        {

            //This (second) call fails with SEC_E_INVALID_HANDLE
            ctxStatus = con->SecureFuncs->InitializeSecurityContextW(
                &con->CredHandle,
                &con->ContextHandle,
                NULL,
                flags,
                0,
                0,
                &inDesc,
                0,
                NULL,
                &outDesc,
                &flags,
                NULL
            );
        }

        DbgPrintEx(0, 0, "ctxStatus: %x\n", ctxStatus);

        if (inBuf[1].BufferType == SECBUFFER_EXTRA)
        {
            RtlCopyMemory(
                con->Incoming,
                con->Incoming + (con->Received - inBuf[1].cbBuffer),
                inBuf[1].cbBuffer
            );

            con->Received = inBuf[1].cbBuffer;
        }
        else
        {
            con->Received = 0;
        }

        switch (ctxStatus)
        {

        case SEC_E_INCOMPLETE_MESSAGE:

            break;

        case SEC_I_CONTINUE_NEEDED:

            if (!outBuf[0].pvBuffer)
            {
                retVal = STATUS_NO_MEMORY;
                continue;
            }

            retVal = SocketSend(
                con->SocketConnection,
                outBuf[0].pvBuffer,
                outBuf[0].cbBuffer
            );

            con->SecureFuncs->FreeContextBuffer(outBuf[0].pvBuffer);

            if (!NT_SUCCESS(retVal))
                continue;

            break;

        case SEC_E_OK:

            retVal = STATUS_SUCCESS;
            *outHandle = con;
            continue;

        default:

            retVal = STATUS_UNSUCCESSFUL;
            continue;

        }

        if (con->Received == TLS_MAX_PACKET_SIZE)
        {
            retVal = STATUS_INVALID_BUFFER_SIZE;
            continue;
        }

        retVal = SocketRecieve(
            con->SocketConnection,
            TLS_MAX_PACKET_SIZE - con->Received,
            con->Incoming + con->Received,
            &downloaded
        );

        if (NT_SUCCESS(retVal))
        {
            con->Received += downloaded;
            retVal = STATUS_PENDING;
        }
    }

Done:

    //Freeing resources in case of error
    if (!NT_SUCCESS(retVal) && con)
    {
        if (con->SecureFuncs && destroyCtx)
            //This causes a BSOD, even though the ContextHandle should be valid.
            con->SecureFuncs->DeleteSecurityContext(&con->ContextHandle);

        if (con->SecureFuncs && credStatus == SEC_E_OK)
            con->SecureFuncs->FreeCredentialsHandle(&con->CredHandle);

        if (con->SocketConnection)
            SocketCloseConnectionHandle(con->SocketConnection);

        FreeUserMemory(con);
    }

    return retVal;
}
c winapi windows-kernel schannel
© www.soinside.com 2019 - 2024. All rights reserved.