Expose certificate request type in SslServerCredentials.

pull/16552/head
Jan Tattermusch 7 years ago
parent cd74b357e1
commit db0e21a5cb
  1. 6
      src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs
  2. 4
      src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs
  3. 97
      src/csharp/Grpc.Core/ServerCredentials.cs
  4. 143
      src/csharp/Grpc.IntegrationTesting/SslCredentialsTest.cs
  5. 6
      src/csharp/ext/grpc_csharp_ext.c
  6. 2
      templates/src/csharp/Grpc.Core/Internal/NativeMethods.Generated.cs.template

@ -505,7 +505,7 @@ namespace Grpc.Core.Internal
public delegate void grpcsharp_redirect_log_delegate(GprLogDelegate callback);
public delegate CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin_delegate(NativeMetadataInterceptor interceptor);
public delegate void grpcsharp_metadata_credentials_notify_from_plugin_delegate(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth);
public delegate ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create_delegate(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, SslClientCertificateRequestType clientCertificateRequest);
public delegate void grpcsharp_server_credentials_release_delegate(IntPtr credentials);
public delegate ServerSafeHandle grpcsharp_server_create_delegate(ChannelArgsSafeHandle args);
public delegate void grpcsharp_server_register_completion_queue_delegate(ServerSafeHandle server, CompletionQueueSafeHandle cq);
@ -752,7 +752,7 @@ namespace Grpc.Core.Internal
public static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
[DllImport(ImportName)]
public static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth);
public static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, SslClientCertificateRequestType clientCertificateRequest);
[DllImport(ImportName)]
public static extern void grpcsharp_server_credentials_release(IntPtr credentials);
@ -1045,7 +1045,7 @@ namespace Grpc.Core.Internal
public static extern void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails);
[DllImport(ImportName)]
public static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth);
public static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, SslClientCertificateRequestType clientCertificateRequest);
[DllImport(ImportName)]
public static extern void grpcsharp_server_credentials_release(IntPtr credentials);

@ -32,13 +32,13 @@ namespace Grpc.Core.Internal
{
}
public static ServerCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, bool forceClientAuth)
public static ServerCredentialsSafeHandle CreateSslCredentials(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, SslClientCertificateRequestType clientCertificateRequest)
{
GrpcPreconditions.CheckArgument(keyCertPairCertChainArray.Length == keyCertPairPrivateKeyArray.Length);
return Native.grpcsharp_ssl_server_credentials_create(pemRootCerts,
keyCertPairCertChainArray, keyCertPairPrivateKeyArray,
new UIntPtr((ulong)keyCertPairCertChainArray.Length),
forceClientAuth ? 1 : 0);
clientCertificateRequest);
}
protected override bool ReleaseHandle()

@ -57,6 +57,60 @@ namespace Grpc.Core
}
}
/// <summary>
/// Modes of requesting client's SSL certificate by the server.
/// Corresponds to <c>grpc_ssl_client_certificate_request_type</c>.
/// </summary>
public enum SslClientCertificateRequestType {
/// <summary>
/// Server does not request client certificate.
/// The certificate presented by the client is not checked by the server at
/// all. (A client may present a self signed or signed certificate or not
/// present a certificate at all and any of those option would be accepted)
/// </summary>
DontRequestClientCertificate = 0,
/// <summary>
/// Server requests client certificate but does not enforce that the client
/// presents a certificate.
/// If the client presents a certificate, the client authentication is left to
/// the application (the necessary metadata will be available to the
/// application via authentication context properties, see grpc_auth_context).
/// The client's key certificate pair must be valid for the SSL connection to
/// be established.
///</summary>
RequestClientCertificateButDontVerify,
/// <summary>
/// Server requests client certificate but does not enforce that the client
/// presents a certificate.
/// If the client presents a certificate, the client authentication is done by
/// the gRPC framework. (For a successful connection the client needs to either
/// present a certificate that can be verified against the root certificate
/// configured by the server or not present a certificate at all)
/// The client's key certificate pair must be valid for the SSL connection to
/// be established.
/// </summary>
RequestClientCertificateAndVerify,
/// <summary>
/// Server requests client certificate and enforces that the client presents a
/// certificate.
/// If the client presents a certificate, the client authentication is left to
/// the application (the necessary metadata will be available to the
/// application via authentication context properties, see grpc_auth_context).
/// The client's key certificate pair must be valid for the SSL connection to
/// be established.
///</summary>
RequestAndRequireClientCertificateButDontVerify,
/// <summary>
/// Server requests client certificate and enforces that the client presents a
/// certificate.
/// The cerificate presented by the client is verified by the gRPC framework.
/// (For a successful connection the client needs to present a certificate that
/// can be verified against the root certificate configured by the server)
/// The client's key certificate pair must be valid for the SSL connection to
/// be established.
/// </summary>
RequestAndRequireClientCertificateAndVerify,
}
/// <summary>
/// Server-side SSL credentials.
/// </summary>
@ -64,35 +118,45 @@ namespace Grpc.Core
{
readonly IList<KeyCertificatePair> keyCertificatePairs;
readonly string rootCertificates;
readonly bool forceClientAuth;
readonly SslClientCertificateRequestType clientCertificateRequest;
/// <summary>
/// Creates server-side SSL credentials.
/// </summary>
/// <param name="keyCertificatePairs">Key-certificates to use.</param>
/// <param name="rootCertificates">PEM encoded client root certificates used to authenticate client.</param>
/// <param name="forceClientAuth">If true, client will be rejected unless it proves its unthenticity using against rootCertificates.</param>
/// <param name="forceClientAuth">Deprecated, use clientCertificateRequest overload instead.</param>
public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs, string rootCertificates, bool forceClientAuth)
: this(keyCertificatePairs, rootCertificates, forceClientAuth ? SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify : SslClientCertificateRequestType.DontRequestClientCertificate)
{
}
/// <summary>
/// Creates server-side SSL credentials.
/// </summary>
/// <param name="keyCertificatePairs">Key-certificates to use.</param>
/// <param name="rootCertificates">PEM encoded client root certificates used to authenticate client.</param>
/// <param name="clientCertificateRequest">Options for requesting and verification of client certificate.</param>
public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs, string rootCertificates, SslClientCertificateRequestType clientCertificateRequest)
{
this.keyCertificatePairs = new List<KeyCertificatePair>(keyCertificatePairs).AsReadOnly();
GrpcPreconditions.CheckArgument(this.keyCertificatePairs.Count > 0,
"At least one KeyCertificatePair needs to be provided.");
if (forceClientAuth)
if (clientCertificateRequest == SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify)
{
GrpcPreconditions.CheckNotNull(rootCertificates,
"Cannot force client authentication unless you provide rootCertificates.");
"Cannot require and verify client certificate unless you provide rootCertificates.");
}
this.rootCertificates = rootCertificates;
this.forceClientAuth = forceClientAuth;
this.clientCertificateRequest = clientCertificateRequest;
}
/// <summary>
/// Creates server-side SSL credentials.
/// This constructor should be use if you do not wish to autheticate client
/// using client root certificates.
/// This constructor should be use if you do not wish to autheticate client at all.
/// </summary>
/// <param name="keyCertificatePairs">Key-certificates to use.</param>
public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs) : this(keyCertificatePairs, null, false)
public SslServerCredentials(IEnumerable<KeyCertificatePair> keyCertificatePairs) : this(keyCertificatePairs, null, SslClientCertificateRequestType.DontRequestClientCertificate)
{
}
@ -119,13 +183,24 @@ namespace Grpc.Core
}
/// <summary>
/// If true, the authenticity of client check will be enforced.
/// Deprecated. If true, the authenticity of client check will be enforced.
/// </summary>
public bool ForceClientAuthentication
{
get
{
return this.forceClientAuth;
return this.clientCertificateRequest == SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify;
}
}
/// <summary>
/// Mode of requesting certificate from client by the server.
/// </summary>
public SslClientCertificateRequestType ClientCertificateRequest
{
get
{
return this.clientCertificateRequest;
}
}
@ -139,7 +214,7 @@ namespace Grpc.Core
certChains[i] = keyCertificatePairs[i].CertificateChain;
keys[i] = keyCertificatePairs[i].PrivateKey;
}
return ServerCredentialsSafeHandle.CreateSslCredentials(rootCertificates, certChains, keys, forceClientAuth);
return ServerCredentialsSafeHandle.CreateSslCredentials(rootCertificates, certChains, keys, clientCertificateRequest);
}
}
}

@ -37,20 +37,24 @@ namespace Grpc.IntegrationTesting
public class SslCredentialsTest
{
const string Host = "localhost";
const string IsPeerAuthenticatedMetadataKey = "test_only_is_peer_authenticated";
Server server;
Channel channel;
TestService.TestServiceClient client;
[OneTimeSetUp]
public void Init()
string rootCert;
KeyCertificatePair keyCertPair;
public void InitClientAndServer(bool clientAddKeyCertPair,
SslClientCertificateRequestType clientCertRequestType)
{
var rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath);
var keyCertPair = new KeyCertificatePair(
rootCert = File.ReadAllText(TestCredentials.ClientCertAuthorityPath);
keyCertPair = new KeyCertificatePair(
File.ReadAllText(TestCredentials.ServerCertChainPath),
File.ReadAllText(TestCredentials.ServerPrivateKeyPath));
var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, true);
var clientCredentials = new SslCredentials(rootCert, keyCertPair);
var serverCredentials = new SslServerCredentials(new[] { keyCertPair }, rootCert, clientCertRequestType);
var clientCredentials = clientAddKeyCertPair ? new SslCredentials(rootCert, keyCertPair) : new SslCredentials(rootCert);
// Disable SO_REUSEPORT to prevent https://github.com/grpc/grpc/issues/10755
server = new Server(new[] { new ChannelOption(ChannelOptions.SoReuseport, 0) })
@ -72,19 +76,133 @@ namespace Grpc.IntegrationTesting
[OneTimeTearDown]
public void Cleanup()
{
channel.ShutdownAsync().Wait();
server.ShutdownAsync().Wait();
if (channel != null)
{
channel.ShutdownAsync().Wait();
}
if (server != null)
{
server.ShutdownAsync().Wait();
}
}
[Test]
public void AuthenticatedClientAndServer()
public async Task NoClientCert_DontRequestClientCertificate_Accepted()
{
var response = client.UnaryCall(new SimpleRequest { ResponseSize = 10 });
Assert.AreEqual(10, response.Payload.Body.Length);
InitClientAndServer(
clientAddKeyCertPair: false,
clientCertRequestType: SslClientCertificateRequestType.DontRequestClientCertificate);
await CheckAccepted(expectPeerAuthenticated: false);
}
[Test]
public async Task AuthContextIsPopulated()
public async Task ClientWithCert_DontRequestClientCertificate_AcceptedButPeerNotAuthenticated()
{
InitClientAndServer(
clientAddKeyCertPair: true,
clientCertRequestType: SslClientCertificateRequestType.DontRequestClientCertificate);
await CheckAccepted(expectPeerAuthenticated: false);
}
[Test]
public async Task NoClientCert_RequestClientCertificateButDontVerify_Accepted()
{
InitClientAndServer(
clientAddKeyCertPair: false,
clientCertRequestType: SslClientCertificateRequestType.RequestClientCertificateButDontVerify);
await CheckAccepted(expectPeerAuthenticated: false);
}
[Test]
public async Task NoClientCert_RequestClientCertificateAndVerify_Accepted()
{
InitClientAndServer(
clientAddKeyCertPair: false,
clientCertRequestType: SslClientCertificateRequestType.RequestClientCertificateAndVerify);
await CheckAccepted(expectPeerAuthenticated: false);
}
[Test]
public async Task ClientWithCert_RequestAndRequireClientCertificateButDontVerify_Accepted()
{
InitClientAndServer(
clientAddKeyCertPair: true,
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateButDontVerify);
await CheckAccepted(expectPeerAuthenticated: true);
await CheckAuthContextIsPopulated();
}
[Test]
public async Task ClientWithCert_RequestAndRequireClientCertificateAndVerify_Accepted()
{
InitClientAndServer(
clientAddKeyCertPair: true,
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify);
await CheckAccepted(expectPeerAuthenticated: true);
await CheckAuthContextIsPopulated();
}
[Test]
public void NoClientCert_RequestAndRequireClientCertificateButDontVerify_Rejected()
{
InitClientAndServer(
clientAddKeyCertPair: false,
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateButDontVerify);
CheckRejected();
}
[Test]
public void NoClientCert_RequestAndRequireClientCertificateAndVerify_Rejected()
{
InitClientAndServer(
clientAddKeyCertPair: false,
clientCertRequestType: SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify);
CheckRejected();
}
[Test]
public void Constructor_LegacyForceClientAuth()
{
var creds = new SslServerCredentials(new[] { keyCertPair }, rootCert, true);
Assert.AreEqual(SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify, creds.ClientCertificateRequest);
var creds2 = new SslServerCredentials(new[] { keyCertPair }, rootCert, false);
Assert.AreEqual(SslClientCertificateRequestType.DontRequestClientCertificate, creds2.ClientCertificateRequest);
}
[Test]
public void Constructor_NullRootCerts()
{
var keyCertPairs = new[] { keyCertPair };
new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.DontRequestClientCertificate);
new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestClientCertificateAndVerify);
new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireClientCertificateButDontVerify);
Assert.Throws(typeof(ArgumentNullException), () => new SslServerCredentials(keyCertPairs, null, SslClientCertificateRequestType.RequestAndRequireClientCertificateAndVerify));
}
private async Task CheckAccepted(bool expectPeerAuthenticated)
{
var call = client.UnaryCallAsync(new SimpleRequest { ResponseSize = 10 });
var response = await call;
Assert.AreEqual(10, response.Payload.Body.Length);
Assert.AreEqual(expectPeerAuthenticated.ToString(), call.GetTrailers().First((entry) => entry.Key == IsPeerAuthenticatedMetadataKey).Value);
}
private void CheckRejected()
{
var ex = Assert.Throws<RpcException>(() => client.UnaryCall(new SimpleRequest { ResponseSize = 10 }));
Assert.AreEqual(StatusCode.Unavailable, ex.Status.StatusCode);
}
private async Task CheckAuthContextIsPopulated()
{
var call = client.StreamingInputCall();
await call.RequestStream.CompleteAsync();
@ -96,6 +214,7 @@ namespace Grpc.IntegrationTesting
{
public override Task<SimpleResponse> UnaryCall(SimpleRequest request, ServerCallContext context)
{
context.ResponseTrailers.Add(IsPeerAuthenticatedMetadataKey, context.AuthContext.IsPeerAuthenticated.ToString());
return Task.FromResult(new SimpleResponse { Payload = CreateZerosPayload(request.ResponseSize) });
}

@ -964,7 +964,7 @@ GPR_EXPORT grpc_server_credentials* GPR_CALLTYPE
grpcsharp_ssl_server_credentials_create(
const char* pem_root_certs, const char** key_cert_pair_cert_chain_array,
const char** key_cert_pair_private_key_array, size_t num_key_cert_pairs,
int force_client_auth) {
grpc_ssl_client_certificate_request_type client_request_type) {
size_t i;
grpc_server_credentials* creds;
grpc_ssl_pem_key_cert_pair* key_cert_pairs =
@ -981,9 +981,7 @@ grpcsharp_ssl_server_credentials_create(
}
creds = grpc_ssl_server_credentials_create_ex(
pem_root_certs, key_cert_pairs, num_key_cert_pairs,
force_client_auth
? GRPC_SSL_REQUEST_AND_REQUIRE_CLIENT_CERTIFICATE_AND_VERIFY
: GRPC_SSL_DONT_REQUEST_CLIENT_CERTIFICATE,
client_request_type,
NULL);
gpr_free(key_cert_pairs);
return creds;

@ -73,7 +73,7 @@
'void grpcsharp_redirect_log(GprLogDelegate callback)',
'CallCredentialsSafeHandle grpcsharp_metadata_credentials_create_from_plugin(NativeMetadataInterceptor interceptor)',
'void grpcsharp_metadata_credentials_notify_from_plugin(IntPtr callbackPtr, IntPtr userData, MetadataArraySafeHandle metadataArray, StatusCode statusCode, string errorDetails)',
'ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, int forceClientAuth)',
'ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs, SslClientCertificateRequestType clientCertificateRequest)',
'void grpcsharp_server_credentials_release(IntPtr credentials)',
'ServerSafeHandle grpcsharp_server_create(ChannelArgsSafeHandle args)',
'void grpcsharp_server_register_completion_queue(ServerSafeHandle server, CompletionQueueSafeHandle cq)',

Loading…
Cancel
Save