From 97616f7601e6b5a021102fff09c2088cd85f9b6f Mon Sep 17 00:00:00 2001
From: Daiki AMINAKA <1991.daiki@gmail.com>
Date: Sat, 31 Dec 2022 19:09:43 -0800
Subject: [PATCH] new API for async certificate validation (#3318)
* new API for async certificate validation
* Fix func pointer assignment
* generate dotnet
---
...ConnectionCertificateValidationComplete.md | 40 ++++++++++
src/core/api.c | 74 +++++++++++++++++++
src/core/api.h | 8 ++
src/core/connection.c | 7 ++
src/core/library.c | 1 +
src/core/operation.h | 5 ++
src/cs/lib/msquic_generated.cs | 3 +
src/inc/msquic.h | 14 ++++
src/inc/quic_trace.h | 1 +
src/lib.rs | 19 +++++
src/plugins/dbg/quictypes.h | 3 +
src/test/lib/TestConnection.cpp | 6 +-
src/tools/spin/spinquic.cpp | 7 ++
13 files changed, 184 insertions(+), 4 deletions(-)
create mode 100644 docs/api/ConnectionCertificateValidationComplete.md
diff --git a/docs/api/ConnectionCertificateValidationComplete.md b/docs/api/ConnectionCertificateValidationComplete.md
new file mode 100644
index 000000000..5f2cf99b7
--- /dev/null
+++ b/docs/api/ConnectionCertificateValidationComplete.md
@@ -0,0 +1,40 @@
+ConnectionCertificateValidationComplete function
+======
+
+Uses the QUIC (client) handle to complete resumption ticket validation. This must be called after client app handles certificate validation and then return QUIC_STATUS_PENDING.
+
+# Syntax
+
+```C
+typedef
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+(QUIC_API * QUIC_CONNECTION_COMP_CERT_FN)(
+ _In_ _Pre_defensive_ HQUIC Connection,
+ _In_ BOOLEAN Result
+ );
+```
+
+# Parameters
+
+`Connection`
+
+The valid handle to an open connection object.
+
+`Result`
+
+Ticket validation result.
+
+# Return Value
+
+The function returns a [QUIC_STATUS](QUIC_STATUS.md). The app may use `QUIC_FAILED` or `QUIC_SUCCEEDED` to determine if the function failed or succeeded.
+
+# Remarks
+
+- Available from v2.2
+
+# See Also
+
+[ConnectionOpen](ConnectionStart.md)
+[ConnectionClose](ConnectionClose.md)
+[ConnectionShutdown](ConnectionShutdown.md)
diff --git a/src/core/api.c b/src/core/api.c
index 4791845a1..478f14f60 100644
--- a/src/core/api.c
+++ b/src/core/api.c
@@ -1721,3 +1721,77 @@ Error:
return Status;
}
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicConnectionCertificateValidationComplete(
+ _In_ _Pre_defensive_ HQUIC Handle,
+ _In_ BOOLEAN Result
+ )
+{
+ QUIC_STATUS Status;
+ QUIC_CONNECTION* Connection;
+ QUIC_OPERATION* Oper;
+
+ QuicTraceEvent(
+ ApiEnter,
+ "[ api] Enter %u (%p).",
+ QUIC_TRACE_API_CONNECTION_COMPLETE_CERTIFICATE_VALIDATION,
+ Handle);
+
+ if (IS_CONN_HANDLE(Handle)) {
+#pragma prefast(suppress: __WARNING_25024, "Pointer cast already validated.")
+ Connection = (QUIC_CONNECTION*)Handle;
+ } else if (IS_STREAM_HANDLE(Handle)) {
+#pragma prefast(suppress: __WARNING_25024, "Pointer cast already validated.")
+ QUIC_STREAM* Stream = (QUIC_STREAM*)Handle;
+ CXPLAT_TEL_ASSERT(!Stream->Flags.HandleClosed);
+ CXPLAT_TEL_ASSERT(!Stream->Flags.Freed);
+ Connection = Stream->Connection;
+ } else {
+ Status = QUIC_STATUS_INVALID_PARAMETER;
+ goto Error;
+ }
+
+ QUIC_CONN_VERIFY(Connection, !Connection->State.Freed);
+
+ if (QuicConnIsServer(Connection)) {
+ Status = QUIC_STATUS_INVALID_PARAMETER;
+ goto Error;
+ }
+
+ if (Connection->Crypto.TlsState.HandshakeComplete) {
+ Status = QUIC_STATUS_INVALID_STATE;
+ goto Error;
+ }
+
+ Oper = QuicOperationAlloc(Connection->Worker, QUIC_OPER_TYPE_API_CALL);
+ if (Oper == NULL) {
+ Status = QUIC_STATUS_OUT_OF_MEMORY;
+ QuicTraceEvent(
+ AllocFailure,
+ "Allocation of '%s' failed. (%llu bytes)",
+ "CONN_COMPLETE_CERTIFICATE_VALIDATION operation",
+ 0);
+ goto Error;
+ }
+
+ Oper->API_CALL.Context->Type = QUIC_API_TYPE_CONN_COMPLETE_CERTIFICATE_VALIDATION;
+ Oper->API_CALL.Context->CONN_COMPLETE_CERTIFICATE_VALIDATION.Result = Result;
+
+ //
+ // Queue the operation but don't wait for the completion.
+ //
+ QuicConnQueueOper(Connection, Oper);
+ Status = QUIC_STATUS_PENDING;
+
+Error:
+
+ QuicTraceEvent(
+ ApiExitStatus,
+ "[ api] Exit %u",
+ Status);
+
+ return Status;
+}
diff --git a/src/core/api.h b/src/core/api.h
index e1a15f60f..f8736b35d 100644
--- a/src/core/api.h
+++ b/src/core/api.h
@@ -269,3 +269,11 @@ MsQuicConnectionResumptionTicketValidationComplete(
_In_ _Pre_defensive_ HQUIC Handle,
_In_ BOOLEAN Result
);
+
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+QUIC_API
+MsQuicConnectionCertificateValidationComplete(
+ _In_ _Pre_defensive_ HQUIC Handle,
+ _In_ BOOLEAN Result
+ );
diff --git a/src/core/connection.c b/src/core/connection.c
index b01314fa2..c45c4a19c 100644
--- a/src/core/connection.c
+++ b/src/core/connection.c
@@ -7313,6 +7313,13 @@ QuicConnProcessApiOperation(
ApiCtx->CONN_COMPLETE_RESUMPTION_TICKET_VALIDATION.Result);
break;
+ case QUIC_API_TYPE_CONN_COMPLETE_CERTIFICATE_VALIDATION:
+ CXPLAT_DBG_ASSERT(QuicConnIsClient(Connection));
+ QuicCryptoCustomCertValidationComplete(
+ &Connection->Crypto,
+ ApiCtx->CONN_COMPLETE_CERTIFICATE_VALIDATION.Result);
+ break;
+
case QUIC_API_TYPE_STRM_CLOSE:
QuicStreamClose(ApiCtx->STRM_CLOSE.Stream);
break;
diff --git a/src/core/library.c b/src/core/library.c
index 4765ef769..f89264253 100644
--- a/src/core/library.c
+++ b/src/core/library.c
@@ -1669,6 +1669,7 @@ MsQuicOpenVersion(
Api->ConnectionSetConfiguration = MsQuicConnectionSetConfiguration;
Api->ConnectionSendResumptionTicket = MsQuicConnectionSendResumptionTicket;
Api->ConnectionResumptionTicketValidationComplete = MsQuicConnectionResumptionTicketValidationComplete;
+ Api->ConnectionCertificateValidationComplete = MsQuicConnectionCertificateValidationComplete;
Api->StreamOpen = MsQuicStreamOpen;
Api->StreamClose = MsQuicStreamClose;
diff --git a/src/core/operation.h b/src/core/operation.h
index 93fceac8e..6d717cd71 100644
--- a/src/core/operation.h
+++ b/src/core/operation.h
@@ -62,6 +62,7 @@ typedef enum QUIC_API_TYPE {
QUIC_API_TYPE_DATAGRAM_SEND,
QUIC_API_TYPE_CONN_COMPLETE_RESUMPTION_TICKET_VALIDATION,
+ QUIC_API_TYPE_CONN_COMPLETE_CERTIFICATE_VALIDATION,
} QUIC_API_TYPE;
@@ -119,6 +120,10 @@ typedef struct QUIC_API_CONTEXT {
BOOLEAN Result;
} CONN_COMPLETE_RESUMPTION_TICKET_VALIDATION;
+ struct {
+ BOOLEAN Result;
+ } CONN_COMPLETE_CERTIFICATE_VALIDATION;
+
struct {
QUIC_STREAM_OPEN_FLAGS Flags;
QUIC_STREAM_CALLBACK_HANDLER Handler;
diff --git a/src/cs/lib/msquic_generated.cs b/src/cs/lib/msquic_generated.cs
index 2666d1e72..bb4d55d16 100644
--- a/src/cs/lib/msquic_generated.cs
+++ b/src/cs/lib/msquic_generated.cs
@@ -2755,6 +2755,9 @@ namespace Microsoft.Quic
[NativeTypeName("QUIC_CONNECTION_COMP_RESUMPTION_FN")]
internal delegate* unmanaged[Cdecl] ConnectionResumptionTicketValidationComplete;
+
+ [NativeTypeName("QUIC_CONNECTION_COMP_CERT_FN")]
+ internal delegate* unmanaged[Cdecl] ConnectionCertificateValidationComplete;
}
internal static unsafe partial class MsQuic
diff --git a/src/inc/msquic.h b/src/inc/msquic.h
index 836ccd146..26fe47830 100644
--- a/src/inc/msquic.h
+++ b/src/inc/msquic.h
@@ -1279,6 +1279,19 @@ QUIC_STATUS
_In_ BOOLEAN Result
);
+//
+// Uses the QUIC (client) handle to complete certificate validation.
+// This must be called after client app handles certificate validation
+// and then return QUIC_STATUS_PENDING.
+//
+typedef
+_IRQL_requires_max_(DISPATCH_LEVEL)
+QUIC_STATUS
+(QUIC_API * QUIC_CONNECTION_COMP_CERT_FN)(
+ _In_ _Pre_defensive_ HQUIC Connection,
+ _In_ BOOLEAN Result
+ );
+
//
// Streams
//
@@ -1507,6 +1520,7 @@ typedef struct QUIC_API_TABLE {
QUIC_DATAGRAM_SEND_FN DatagramSend;
QUIC_CONNECTION_COMP_RESUMPTION_FN ConnectionResumptionTicketValidationComplete; // Available from v2.2
+ QUIC_CONNECTION_COMP_CERT_FN ConnectionCertificateValidationComplete; // Available from v2.2
} QUIC_API_TABLE;
diff --git a/src/inc/quic_trace.h b/src/inc/quic_trace.h
index 71b1fc053..a77dabfac 100644
--- a/src/inc/quic_trace.h
+++ b/src/inc/quic_trace.h
@@ -89,6 +89,7 @@ typedef enum QUIC_TRACE_API_TYPE {
QUIC_TRACE_API_CONNECTION_SET_CONFIGURATION,
QUIC_TRACE_API_CONNECTION_SEND_RESUMPTION_TICKET,
QUIC_TRACE_API_CONNECTION_COMPLETE_RESUMPTION_TICKET_VALIDATION,
+ QUIC_TRACE_API_CONNECTION_COMPLETE_CERTIFICATE_VALIDATION,
QUIC_TRACE_API_STREAM_OPEN,
QUIC_TRACE_API_STREAM_CLOSE,
QUIC_TRACE_API_STREAM_START,
diff --git a/src/lib.rs b/src/lib.rs
index 07508de45..d220748b9 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -1082,6 +1082,10 @@ struct ApiTable {
connection: Handle,
result: BOOLEAN,
) -> u32,
+ certificate_validation_complete: extern "C" fn(
+ connection: Handle,
+ result: BOOLEAN,
+ ) -> u32,
}
#[link(name = "msquic")]
@@ -1538,6 +1542,21 @@ impl Connection {
panic!("ticket validation completion failure 0x{:x}", status);
}
}
+
+ pub fn certificate_validation_complete(
+ &self,
+ result: BOOLEAN,
+ ) {
+ let status = unsafe {
+ ((*self.table).certificate_validation_complete)(
+ self.handle,
+ result,
+ )
+ };
+ if Status::failed(status) {
+ panic!("ticket validation completion failure 0x{:x}", status);
+ }
+ }
}
impl Drop for Connection {
diff --git a/src/plugins/dbg/quictypes.h b/src/plugins/dbg/quictypes.h
index 058e31a62..d838fc336 100644
--- a/src/plugins/dbg/quictypes.h
+++ b/src/plugins/dbg/quictypes.h
@@ -993,6 +993,7 @@ typedef enum QUIC_API_TYPE {
QUIC_API_TYPE_DATAGRAM_SEND,
QUIC_API_TYPE_CONN_COMPLETE_RESUMPTION_TICKET_VALIDATION,
+ QUIC_API_TYPE_CONN_COMPLETE_CERTIFICATE_VALIDATION,
} QUIC_API_TYPE;
@@ -1036,6 +1037,8 @@ struct ApiCall : Struct {
return "API_TYPE_DATAGRAM_SEND";
case QUIC_API_TYPE_CONN_COMPLETE_RESUMPTION_TICKET_VALIDATION:
return "API_TYPE_CONN_COMPLETE_RESUMPTION_TICKET_VALIDATION";
+ case QUIC_API_TYPE_CONN_COMPLETE_CERTIFICATE_VALIDATION:
+ return "API_TYPE_CONN_COMPLETE_CERTIFICATE_VALIDATION";
default:
return "INVALID API";
}
diff --git a/src/test/lib/TestConnection.cpp b/src/test/lib/TestConnection.cpp
index c9715eaf4..f14b1fc75 100644
--- a/src/test/lib/TestConnection.cpp
+++ b/src/test/lib/TestConnection.cpp
@@ -751,11 +751,9 @@ TestConnection::SetCustomValidationResult(
{
BOOLEAN Result = AcceptCert ? TRUE : FALSE;
return
- MsQuic->SetParam(
+ MsQuic->ConnectionCertificateValidationComplete(
QuicConnection,
- QUIC_PARAM_CONN_PEER_CERTIFICATE_VALID,
- sizeof(Result),
- &Result);
+ Result);
}
QUIC_STATUS
diff --git a/src/tools/spin/spinquic.cpp b/src/tools/spin/spinquic.cpp
index 001adb12f..12b528573 100644
--- a/src/tools/spin/spinquic.cpp
+++ b/src/tools/spin/spinquic.cpp
@@ -258,6 +258,7 @@ typedef enum {
SpinQuicAPICallGetParamStream,
SpinQuicAPICallDatagramSend,
SpinQuicAPICallCompleteTicketValidation,
+ SpinQuicAPICallCompleteCertificateValidation,
SpinQuicAPICallStreamReceiveSetEnabled,
SpinQuicAPICallStreamReceiveComplete,
SpinQuicAPICallCount // Always the last element
@@ -881,6 +882,12 @@ void Spin(Gbs& Gb, LockableVector& Connections, std::vector* Liste
MsQuic.ConnectionResumptionTicketValidationComplete(Connection, GetRandom(2) == 0);
break;
}
+ case SpinQuicAPICallCompleteCertificateValidation: {
+ auto Connection = Connections.TryGetRandom();
+ BAIL_ON_NULL_CONNECTION(Connection);
+ MsQuic.ConnectionCertificateValidationComplete(Connection, GetRandom(2) == 0);
+ break;
+ }
default:
break;
}