diff --git a/include/grpc++/channel_arguments.h b/include/grpc++/channel_arguments.h index b649ba23b85509f8a97749c444791158dad11b54..8d338c654ec5ae171e914ce36ddba04db561031d 100644 --- a/include/grpc++/channel_arguments.h +++ b/include/grpc++/channel_arguments.h @@ -66,7 +66,7 @@ class ChannelArguments { void SetChannelArgs(grpc_channel_args* channel_args) const; private: - friend class Channel; + friend class SecureCredentials; friend class testing::ChannelArgumentsTest; // TODO(yangg) implement copy and assign diff --git a/include/grpc/grpc_security.h b/include/grpc/grpc_security.h index ab2cc08489ddd7629648dc6071874b2a3c220b5d..c297622a512bf32891b871a77502c2471a3284ba 100644 --- a/include/grpc/grpc_security.h +++ b/include/grpc/grpc_security.h @@ -167,10 +167,9 @@ grpc_server_credentials *grpc_ssl_server_credentials_create( grpc_server_credentials *grpc_fake_transport_security_server_credentials_create( void); -/* --- Secure server creation. --- */ +/* --- Server-side secure ports. --- */ /* Add a HTTP2 over an encrypted link over tcp listener. - Server must have been created with grpc_secure_server_create. Returns bound port number on success, 0 on failure. REQUIRES: server not started */ int grpc_server_add_secure_http2_port(grpc_server *server, const char *addr, diff --git a/src/cpp/client/secure_credentials.cc b/src/cpp/client/secure_credentials.cc index 5eb5c547942bc97a19edd788421aa3ed9f91351c..47f645c1b6366d9698470a5f79ce974df588648a 100644 --- a/src/cpp/client/secure_credentials.cc +++ b/src/cpp/client/secure_credentials.cc @@ -54,7 +54,8 @@ class SecureCredentials GRPC_FINAL : public Credentials { grpc_channel_args channel_args; args.SetChannelArgs(&channel_args); return std::shared_ptr<ChannelInterface>(new Channel( - target, + args.GetSslTargetNameOverride().empty() + ? target : args.GetSslTargetNameOverride(), grpc_secure_channel_create(c_creds_, target.c_str(), &channel_args))); } diff --git a/src/csharp/Grpc.Core/Grpc.Core.csproj b/src/csharp/Grpc.Core/Grpc.Core.csproj index 78b6cdde59bc11f7052a290d3d15c04e8141b310..c4b12b1cab03545697417a7b7275d5e4abf8628b 100644 --- a/src/csharp/Grpc.Core/Grpc.Core.csproj +++ b/src/csharp/Grpc.Core/Grpc.Core.csproj @@ -74,6 +74,8 @@ <Compile Include="OperationFailedException.cs" /> <Compile Include="Internal\AsyncCall.cs" /> <Compile Include="Utils\Preconditions.cs" /> + <Compile Include="Internal\ServerCredentialsSafeHandle.cs" /> + <Compile Include="ServerCredentials.cs" /> </ItemGroup> <Choose> <!-- Under older versions of Monodevelop, Choose is not supported and is just diff --git a/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs new file mode 100644 index 0000000000000000000000000000000000000000..961180741a95202e9a89e8473afcf4668bbb1424 --- /dev/null +++ b/src/csharp/Grpc.Core/Internal/ServerCredentialsSafeHandle.cs @@ -0,0 +1,68 @@ +#region Copyright notice and license +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +#endregion +using System; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Grpc.Core.Utils; + +namespace Grpc.Core.Internal +{ + /// <summary> + /// grpc_server_credentials from <grpc/grpc_security.h> + /// </summary> + internal class ServerCredentialsSafeHandle : SafeHandleZeroIsInvalid + { + [DllImport("grpc_csharp_ext.dll", CharSet = CharSet.Ansi)] + static extern ServerCredentialsSafeHandle grpcsharp_ssl_server_credentials_create(string pemRootCerts, string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray, UIntPtr numKeyCertPairs); + + [DllImport("grpc_csharp_ext.dll")] + static extern void grpcsharp_server_credentials_release(IntPtr credentials); + + private ServerCredentialsSafeHandle() + { + } + + public static ServerCredentialsSafeHandle CreateSslCredentials(string[] keyCertPairCertChainArray, string[] keyCertPairPrivateKeyArray) + { + Preconditions.CheckArgument(keyCertPairCertChainArray.Length == keyCertPairPrivateKeyArray.Length); + return grpcsharp_ssl_server_credentials_create(null, + keyCertPairCertChainArray, keyCertPairPrivateKeyArray, + new UIntPtr((ulong)keyCertPairCertChainArray.Length)); + } + + protected override bool ReleaseHandle() + { + grpcsharp_server_credentials_release(handle); + return true; + } + } +} diff --git a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs index de9bbaf7c123587e495f6bcfc17c2238ec41c75b..b5a5ae4976275f427599b64af39a5229deae381a 100644 --- a/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs +++ b/src/csharp/Grpc.Core/Internal/ServerSafeHandle.cs @@ -55,6 +55,9 @@ namespace Grpc.Core.Internal [DllImport("grpc_csharp_ext.dll")] static extern Int32 grpcsharp_server_add_http2_port(ServerSafeHandle server, string addr); + [DllImport("grpc_csharp_ext.dll")] + static extern Int32 grpcsharp_server_add_secure_http2_port(ServerSafeHandle server, string addr, ServerCredentialsSafeHandle creds); + [DllImport("grpc_csharp_ext.dll")] static extern void grpcsharp_server_start(ServerSafeHandle server); @@ -74,7 +77,6 @@ namespace Grpc.Core.Internal public static ServerSafeHandle NewServer(CompletionQueueSafeHandle cq, IntPtr args) { - // TODO: also grpc_secure_server_create... return grpcsharp_server_create(cq, args); } @@ -83,6 +85,11 @@ namespace Grpc.Core.Internal return grpcsharp_server_add_http2_port(this, addr); } + public int AddPort(string addr, ServerCredentialsSafeHandle credentials) + { + return grpcsharp_server_add_secure_http2_port(this, addr, credentials); + } + public void Start() { grpcsharp_server_start(this); diff --git a/src/csharp/Grpc.Core/Server.cs b/src/csharp/Grpc.Core/Server.cs index 152cc2176c094ecc79e4e29aed3d3f62d4f704e9..cafdb3b6637bb2614c5a401ad8731e6bd1d830fa 100644 --- a/src/csharp/Grpc.Core/Server.cs +++ b/src/csharp/Grpc.Core/Server.cs @@ -75,10 +75,20 @@ namespace Grpc.Core } // only call before Start() - public int AddPort(string addr) { + public int AddPort(string addr) + { return handle.AddPort(addr); } + // only call before Start() + public int AddPort(string addr, ServerCredentials credentials) + { + using (var nativeCredentials = credentials.ToNativeCredentials()) + { + return handle.AddPort(addr, nativeCredentials); + } + } + public void Start() { handle.Start(); diff --git a/src/csharp/Grpc.Core/ServerCredentials.cs b/src/csharp/Grpc.Core/ServerCredentials.cs new file mode 100644 index 0000000000000000000000000000000000000000..1372e61fa71990bb7f1160831877001ec621743c --- /dev/null +++ b/src/csharp/Grpc.Core/ServerCredentials.cs @@ -0,0 +1,107 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using Grpc.Core.Internal; + +namespace Grpc.Core +{ + public abstract class ServerCredentials + { + /// <summary> + /// Creates native object for the credentials. + /// </summary> + /// <returns>The native credentials.</returns> + internal abstract ServerCredentialsSafeHandle ToNativeCredentials(); + } + + /// <summary> + /// Key certificate pair (in PEM encoding). + /// </summary> + public class KeyCertificatePair + { + string certChain; + string privateKey; + + public KeyCertificatePair(string certChain, string privateKey) + { + this.certChain = certChain; + this.privateKey = privateKey; + } + + public string CertChain + { + get + { + return certChain; + } + } + + public string PrivateKey + { + get + { + return privateKey; + } + } + } + + /// <summary> + /// Server-side SSL credentials. + /// </summary> + public class SslServerCredentials : ServerCredentials + { + // TODO: immutable list... + List<KeyCertificatePair> keyCertPairs; + + public SslServerCredentials(List<KeyCertificatePair> keyCertPairs) + { + this.keyCertPairs = keyCertPairs; + } + + internal override ServerCredentialsSafeHandle ToNativeCredentials() + { + int count = keyCertPairs.Count; + string[] certChains = new string[count]; + string[] keys = new string[count]; + for (int i = 0; i < count; i++) + { + certChains[i] = keyCertPairs[i].CertChain; + keys[i] = keyCertPairs[i].PrivateKey; + } + return ServerCredentialsSafeHandle.CreateSslCredentials(certChains, keys); + } + } +} + diff --git a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj index 8f7a17efcb187be00ac1163e0185dc46b6b7066f..438bf9e95de6ab8715f6d845b3fcf4d2c082faa7 100644 --- a/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj +++ b/src/csharp/Grpc.IntegrationTesting/Grpc.IntegrationTesting.csproj @@ -49,6 +49,7 @@ <Compile Include="TestServiceImpl.cs" /> <Compile Include="InteropServer.cs" /> <Compile Include="InteropClient.cs" /> + <Compile Include="TestCredentials.cs" /> </ItemGroup> <Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" /> <ItemGroup> diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs index 30301f165baddff97bafac6a351798453b770e84..2992c42ae95db1710ebc9c5d8b26ca7ec1550213 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClient.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClient.cs @@ -51,7 +51,7 @@ namespace Grpc.IntegrationTesting { public bool help; public string serverHost= "127.0.0.1"; - public string serverHostOverride = "foo.test.google.fr"; + public string serverHostOverride = TestCredentials.DefaultHostOverride; public int? serverPort; public string testCase = "large_unary"; public bool useTls; @@ -103,16 +103,7 @@ namespace Grpc.IntegrationTesting Credentials credentials = null; if (options.useTls) { - string caPath = "data/ca.pem"; // Default testing CA - if (!options.useTestCa) - { - caPath = Environment.GetEnvironmentVariable("SSL_CERT_FILE"); - if (string.IsNullOrEmpty(caPath)) - { - throw new ArgumentException("CA path environment variable is not set."); - } - } - credentials = new SslCredentials(File.ReadAllText(caPath)); + credentials = TestCredentials.CreateTestClientCredentials(options.useTestCa); } ChannelArgs channelArgs = null; diff --git a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs index 4bb0b9ee51fe82bb941c1df704744010ce38b26c..ab2d6f4a6ac1ed31c3e8d2422d8d39ed9d161ba0 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropClientServerTest.cs @@ -59,9 +59,13 @@ namespace Grpc.IntegrationTesting server = new Server(); server.AddServiceDefinition(TestServiceGrpc.BindService(new TestServiceImpl())); - int port = server.AddPort(host + ":0"); + int port = server.AddPort(host + ":0", TestCredentials.CreateTestServerCredentials()); server.Start(); - channel = new Channel(host + ":" + port); + + var channelArgs = ChannelArgs.NewBuilder() + .AddString(ChannelArgs.SslTargetNameOverrideKey, TestCredentials.DefaultHostOverride).Build(); + + channel = new Channel(host + ":" + port, TestCredentials.CreateTestClientCredentials(true), channelArgs); client = TestServiceGrpc.NewStub(channel); } diff --git a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs index a25d3b3530fc5f77ac684a48278b34eac028bad3..24d72da0c3f2b87d3a01d81d974c572314e7e573 100644 --- a/src/csharp/Grpc.IntegrationTesting/InteropServer.cs +++ b/src/csharp/Grpc.IntegrationTesting/InteropServer.cs @@ -34,6 +34,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.IO; using System.Text.RegularExpressions; using System.Threading.Tasks; using Google.ProtocolBuffers; @@ -49,7 +50,7 @@ namespace Grpc.IntegrationTesting private class ServerOptions { public bool help; - public int? port; + public int? port = 8070; public bool useTls; } @@ -93,7 +94,14 @@ namespace Grpc.IntegrationTesting server.AddServiceDefinition(TestServiceGrpc.BindService(new TestServiceImpl())); string addr = "0.0.0.0:" + options.port; - server.AddPort(addr); + if (options.useTls) + { + server.AddPort(addr, TestCredentials.CreateTestServerCredentials()); + } + else + { + server.AddPort(addr); + } Console.WriteLine("Running server on " + addr); server.Start(); diff --git a/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs new file mode 100644 index 0000000000000000000000000000000000000000..b31abf118118fc2e992c32c237e7844da7b5462f --- /dev/null +++ b/src/csharp/Grpc.IntegrationTesting/TestCredentials.cs @@ -0,0 +1,83 @@ +#region Copyright notice and license + +// Copyright 2015, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using Google.ProtocolBuffers; +using Grpc.Core; +using Grpc.Core.Utils; +using NUnit.Framework; +using grpc.testing; + +namespace Grpc.IntegrationTesting +{ + /// <summary> + /// SSL Credentials for testing. + /// </summary> + public static class TestCredentials + { + public const string DefaultHostOverride = "foo.test.google.fr"; + + public const string ClientCertAuthorityPath = "data/ca.pem"; + public const string ClientCertAuthorityEnvName = "SSL_CERT_FILE"; + + public const string ServerCertChainPath = "data/server1.pem"; + public const string ServerPrivateKeyPath = "data/server1.key"; + + public static SslCredentials CreateTestClientCredentials(bool useTestCa) + { + string caPath = ClientCertAuthorityPath; + if (!useTestCa) + { + caPath = Environment.GetEnvironmentVariable(ClientCertAuthorityEnvName); + if (string.IsNullOrEmpty(caPath)) + { + throw new ArgumentException("CA path environment variable is not set."); + } + } + return new SslCredentials(File.ReadAllText(caPath)); + } + + public static SslServerCredentials CreateTestServerCredentials() + { + var keyCertPair = new KeyCertificatePair( + File.ReadAllText(ServerCertChainPath), + File.ReadAllText(ServerPrivateKeyPath)); + return new SslServerCredentials(new List<KeyCertificatePair> {keyCertPair}); + } + } +} diff --git a/src/csharp/ext/grpc_csharp_ext.c b/src/csharp/ext/grpc_csharp_ext.c index e24438704cfb0fc5085318e323ee622027bbbfbd..51abb632f7a195657feefb3f6b9b33baf204d612 100644 --- a/src/csharp/ext/grpc_csharp_ext.c +++ b/src/csharp/ext/grpc_csharp_ext.c @@ -653,6 +653,41 @@ grpcsharp_secure_channel_create(grpc_credentials *creds, const char *target, return grpc_secure_channel_create(creds, target, args); } +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) { + size_t i; + grpc_server_credentials *creds; + grpc_ssl_pem_key_cert_pair *key_cert_pairs = + gpr_malloc(sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); + memset(key_cert_pairs, 0, + sizeof(grpc_ssl_pem_key_cert_pair) * num_key_cert_pairs); + + for (i = 0; i < num_key_cert_pairs; i++) { + if (key_cert_pair_cert_chain_array[i] || + key_cert_pair_private_key_array[i]) { + key_cert_pairs[i].cert_chain = key_cert_pair_cert_chain_array[i]; + key_cert_pairs[i].private_key = key_cert_pair_private_key_array[i]; + } + } + creds = grpc_ssl_server_credentials_create(pem_root_certs, key_cert_pairs, + num_key_cert_pairs); + gpr_free(key_cert_pairs); + return creds; +} + +GPR_EXPORT void grpcsharp_server_credentials_release( + grpc_server_credentials *creds) { + grpc_server_credentials_release(creds); +} + +GPR_EXPORT gpr_int32 GPR_CALLTYPE +grpcsharp_server_add_secure_http2_port(grpc_server *server, const char *addr, + grpc_server_credentials *creds) { + return grpc_server_add_secure_http2_port(server, addr, creds); +} + /* Logging */ typedef void(GPR_CALLTYPE *grpcsharp_log_func)(const char *file, gpr_int32 line, diff --git a/tools/dockerfile/grpc_csharp_mono/Dockerfile b/tools/dockerfile/grpc_csharp_mono/Dockerfile index 8f8636656062640c47f79a5a476ba7a63db711fa..703b658a23b41539361cef67b4308d3857462dc3 100644 --- a/tools/dockerfile/grpc_csharp_mono/Dockerfile +++ b/tools/dockerfile/grpc_csharp_mono/Dockerfile @@ -51,5 +51,5 @@ ADD cacerts cacerts # Add a service_account directory containing the auth creds file ADD service_account service_account -# TODO: add command to run the interop server -CMD ["/bin/bash", "-l"] +# Run the C# Interop Server +CMD ["/bin/bash", "-l", "-c", "cd /var/local/git/grpc/src/csharp/Grpc.IntegrationTesting.Server/bin/Debug && mono Grpc.IntegrationTesting.Server.exe --use_tls=true --port=8070"]