Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix Client Cert Tests (Windows Kernel) #1652

Merged
merged 9 commits into from
Jun 12, 2021
2 changes: 1 addition & 1 deletion .azure/azure-pipelines.ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -447,7 +447,7 @@ stages:
pool: MsQuic-Win-Latest
platform: windows
tls: schannel
logProfile: Full.Light
logProfile: Full.Verbose
nibanks marked this conversation as resolved.
Show resolved Hide resolved
extraArgs: -Kernel -Filter -*NthAllocFail*
kernel: true
testCerts: true
Expand Down
129 changes: 78 additions & 51 deletions src/platform/tls_schannel.c
Original file line number Diff line number Diff line change
Expand Up @@ -396,6 +396,17 @@ typedef struct CXPLAT_SEC_CONFIG {
//
CXPLAT_TLS_CALLBACKS Callbacks;

#ifdef _KERNEL_MODE
//
// Impersonation token from the original call to initialize.
//
PACCESS_TOKEN ImpersonationToken;
BOOLEAN IsPrimaryToken;
BOOLEAN CopyOnOpen;
BOOLEAN EffectiveOnly;
SECURITY_IMPERSONATION_LEVEL ImpersonationLevel;
#endif // _KERNEL_MODE

} CXPLAT_SEC_CONFIG;

typedef struct QUIC_ACH_CONTEXT {
Expand Down Expand Up @@ -1249,62 +1260,27 @@ CxPlatTlsSecConfigCreate(
goto Error;
}

CXPLAT_DBG_ASSERT(AchContext->SecConfig != NULL);
AchContext->SecConfig->ImpersonationToken =
PsReferenceImpersonationToken(
PsGetCurrentThread(),
&AchContext->SecConfig->CopyOnOpen,
&AchContext->SecConfig->EffectiveOnly,
&AchContext->SecConfig->ImpersonationLevel);

if (AchContext->SecConfig->ImpersonationToken == NULL) {
AchContext->SecConfig->ImpersonationToken =
PsReferencePrimaryToken(
PsGetCurrentProcess());
AchContext->SecConfig->IsPrimaryToken = TRUE;
}
nibanks marked this conversation as resolved.
Show resolved Hide resolved

QuicTraceLogVerbose(
SchannelAchWorkerStart,
"[ tls] Starting ACH worker");

TLS_WORKER_CONTEXT ThreadContext = { STATUS_SUCCESS, AchContext };
if (IsClient) {
//
// For schannel resumption to work, we have to call the client side
// of this from a SYSTEM thread.
//
HANDLE ThreadHandle;
Status =
PsCreateSystemThread(
&ThreadHandle,
THREAD_ALL_ACCESS,
NULL,
NULL,
NULL,
CxPlatTlsAchWorker,
&ThreadContext);
if (QUIC_FAILED(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"PsCreateSystemThread(CxPlatTlsAchWorker)");
goto Error;
}
void* Thread = NULL;
Status =
ObReferenceObjectByHandle(
ThreadHandle,
THREAD_ALL_ACCESS,
*PsThreadType,
KernelMode,
&Thread,
NULL);
ZwClose(ThreadHandle);
if (QUIC_FAILED(Status)) {
QuicTraceEvent(
LibraryErrorStatus,
"[ lib] ERROR, %u, %s.",
Status,
"ObReferenceObjectByHandle(CxPlatTlsAchWorker)");
goto Error;
}
KeWaitForSingleObject(Thread, Executive, KernelMode, FALSE, NULL);
ObDereferenceObject(Thread);

} else {
//
// For schannel to successfully load the certificate (even a machine
// one), this needs to be on the caller's thread.
//
CxPlatTlsAchHelper(&ThreadContext);
}
CxPlatTlsAchHelper(&ThreadContext);

Status = ThreadContext.CompletionStatus;
AchContext = ThreadContext.AchContext;
Expand Down Expand Up @@ -1384,6 +1360,16 @@ CxPlatTlsSecConfigDelete(
FreeCredentialsHandle(&ServerConfig->CredentialHandle);
}

#ifdef _KERNEL_MODE
if (ServerConfig->ImpersonationToken) {
if (ServerConfig->IsPrimaryToken) {
PsDereferencePrimaryToken(ServerConfig->ImpersonationToken);
} else {
PsDereferenceImpersonationToken(ServerConfig->ImpersonationToken);
}
}
#endif

CXPLAT_FREE(ServerConfig, QUIC_POOL_TLS_SECCONF);
}

Expand Down Expand Up @@ -1782,6 +1768,37 @@ CxPlatTlsWriteDataToSchannel(
ULONG ContextAttr;
SECURITY_STATUS SecStatus;

#ifdef _KERNEL_MODE
if (TlsContext->SecConfig->ImpersonationToken) {
NTSTATUS Status;
if (TlsContext->SecConfig->IsPrimaryToken) {
Status =
PsImpersonateClient(
PsGetCurrentThread(),
TlsContext->SecConfig->ImpersonationToken,
FALSE,
FALSE,
SecurityImpersonation);
} else {
Status =
PsImpersonateClient(
PsGetCurrentThread(),
TlsContext->SecConfig->ImpersonationToken,
TlsContext->SecConfig->CopyOnOpen,
TlsContext->SecConfig->EffectiveOnly,
TlsContext->SecConfig->ImpersonationLevel);
}
if (!NT_SUCCESS(Status)) {
QuicTraceEvent(
TlsErrorStatus,
"[ tls][%p] ERROR, %u, %s.",
TlsContext->Connection,
Status,
"PsImpersonateClient failed");
}
}
#endif

if (TlsContext->IsServer) {
CXPLAT_DBG_ASSERT(!(TlsContext->SecConfig->Flags & QUIC_CREDENTIAL_FLAG_CLIENT));

Expand Down Expand Up @@ -1816,6 +1833,16 @@ CxPlatTlsWriteDataToSchannel(
NULL);
}

#ifdef _KERNEL_MODE
if (TlsContext->SecConfig->ImpersonationToken) {
//
// This must only be called on a worker thread.
// Otherwise, this might ruin existing impersonation.
//
PsRevertToSelf();
}
#endif

CXPLAT_TLS_RESULT_FLAGS Result = 0;

SecBuffer* ExtraBuffer = NULL;
Expand Down
5 changes: 2 additions & 3 deletions src/test/bin/quic_gtest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -647,12 +647,11 @@ TEST_P(WithHandshakeArgs5, CustomCertificateValidation) {
TEST_P(WithHandshakeArgs6, ConnectClientCertificate) {
TestLoggerT<ParamType> Logger("QuicTestConnectClientCertificate", GetParam());
if (TestingKernelMode) {
/*QUIC_RUN_CONNECT_CLIENT_CERT Params = {
QUIC_RUN_CONNECT_CLIENT_CERT Params = {
GetParam().Family,
(uint8_t)GetParam().UseClientCertificate
};
ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_CONNECT_CLIENT_CERT, Params));*/
printf("WARNING: ConnectClientCertificate not supported in Kernel Mode yet!\n");
ASSERT_TRUE(DriverClient.Run(IOCTL_QUIC_RUN_CONNECT_CLIENT_CERT, Params));
} else {
QuicTestConnectClientCertificate(GetParam().Family, GetParam().UseClientCertificate);
}
Expand Down