Skip to content
Snippets Groups Projects
Commit 26af0792 authored by Craig Tiller's avatar Craig Tiller
Browse files

Merge pull request #4934 from jboeuf/ssl_credentials_override_default_roots

Ssl credentials override default roots
parents f09fb7e4 2757fa8b
No related branches found
No related tags found
No related merge requests found
...@@ -147,6 +147,29 @@ GRPC_API grpc_channel_credentials *grpc_google_default_credentials_create(void); ...@@ -147,6 +147,29 @@ GRPC_API grpc_channel_credentials *grpc_google_default_credentials_create(void);
#define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \ #define GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR \
"GRPC_DEFAULT_SSL_ROOTS_FILE_PATH" "GRPC_DEFAULT_SSL_ROOTS_FILE_PATH"
/* Results for the SSL roots override callback. */
typedef enum {
GRPC_SSL_ROOTS_OVERRIDE_OK,
GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY, /* Do not try fallback options. */
GRPC_SSL_ROOTS_OVERRIDE_FAIL
} grpc_ssl_roots_override_result;
/* Callback for getting the SSL roots override from the application.
In case of success, *pem_roots_certs must be set to a NULL terminated string
containing the list of PEM encoded root certificates. The ownership is passed
to the core and freed (laster by the core) with gpr_free.
If this function fails and GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment is
set to a valid path, it will override the roots specified this func */
typedef grpc_ssl_roots_override_result (*grpc_ssl_roots_override_callback)(
char **pem_root_certs);
/* Setup a callback to override the default TLS/SSL roots.
This function is not thread-safe and must be called at initialization time
before any ssl credentials are created to have the desired side effect.
If GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment is set to a valid path, the
callback will not be called. */
void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb);
/* Object that holds a private key / certificate chain pair in PEM format. */ /* Object that holds a private key / certificate chain pair in PEM format. */
typedef struct { typedef struct {
/* private_key is the NULL-terminated string containing the PEM encoding of /* private_key is the NULL-terminated string containing the PEM encoding of
...@@ -163,8 +186,9 @@ typedef struct { ...@@ -163,8 +186,9 @@ typedef struct {
of the server root certificates. If this parameter is NULL, the of the server root certificates. If this parameter is NULL, the
implementation will first try to dereference the file pointed by the implementation will first try to dereference the file pointed by the
GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable, and if that fails, GRPC_DEFAULT_SSL_ROOTS_FILE_PATH environment variable, and if that fails,
get the roots from a well-known place on disk (in the grpc install try to get the roots set by grpc_override_ssl_default_roots. Eventually,
directory). if all these fail, it will try to get the roots from a well-known place on
disk (in the grpc install directory).
- pem_key_cert_pair is a pointer on the object containing client's private - pem_key_cert_pair is a pointer on the object containing client's private
key and certificate chain. This parameter can be NULL if the client does key and certificate chain. This parameter can be NULL if the client does
not have such a key/cert pair. */ not have such a key/cert pair. */
......
...@@ -61,6 +61,14 @@ static const char *installed_roots_path = ...@@ -61,6 +61,14 @@ static const char *installed_roots_path =
INSTALL_PREFIX "/share/grpc/roots.pem"; INSTALL_PREFIX "/share/grpc/roots.pem";
#endif #endif
/* -- Overridden default roots. -- */
static grpc_ssl_roots_override_callback ssl_roots_override_cb = NULL;
void grpc_set_ssl_roots_override_callback(grpc_ssl_roots_override_callback cb) {
ssl_roots_override_cb = cb;
}
/* -- Cipher suites. -- */ /* -- Cipher suites. -- */
/* Defines the cipher suites that we accept by default. All these cipher suites /* Defines the cipher suites that we accept by default. All these cipher suites
...@@ -595,23 +603,44 @@ static grpc_security_connector_vtable ssl_channel_vtable = { ...@@ -595,23 +603,44 @@ static grpc_security_connector_vtable ssl_channel_vtable = {
static grpc_security_connector_vtable ssl_server_vtable = { static grpc_security_connector_vtable ssl_server_vtable = {
ssl_server_destroy, ssl_server_do_handshake, ssl_server_check_peer}; ssl_server_destroy, ssl_server_do_handshake, ssl_server_check_peer};
static gpr_slice default_pem_root_certs; static gpr_slice compute_default_pem_root_certs_once(void) {
gpr_slice result = gpr_empty_slice();
static void init_default_pem_root_certs(void) {
/* First try to load the roots from the environment. */ /* First try to load the roots from the environment. */
char *default_root_certs_path = char *default_root_certs_path =
gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR); gpr_getenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR);
if (default_root_certs_path == NULL) { if (default_root_certs_path != NULL) {
default_pem_root_certs = gpr_empty_slice(); result = gpr_load_file(default_root_certs_path, 0, NULL);
} else {
default_pem_root_certs = gpr_load_file(default_root_certs_path, 0, NULL);
gpr_free(default_root_certs_path); gpr_free(default_root_certs_path);
} }
/* Try overridden roots if needed. */
grpc_ssl_roots_override_result ovrd_res = GRPC_SSL_ROOTS_OVERRIDE_FAIL;
if (GPR_SLICE_IS_EMPTY(result) && ssl_roots_override_cb != NULL) {
char *pem_root_certs = NULL;
ovrd_res = ssl_roots_override_cb(&pem_root_certs);
if (ovrd_res == GRPC_SSL_ROOTS_OVERRIDE_OK) {
GPR_ASSERT(pem_root_certs != NULL);
result = gpr_slice_new(pem_root_certs, strlen(pem_root_certs), gpr_free);
}
}
/* Fall back to installed certs if needed. */ /* Fall back to installed certs if needed. */
if (GPR_SLICE_IS_EMPTY(default_pem_root_certs)) { if (GPR_SLICE_IS_EMPTY(result) &&
default_pem_root_certs = gpr_load_file(installed_roots_path, 0, NULL); ovrd_res != GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY) {
result = gpr_load_file(installed_roots_path, 0, NULL);
} }
return result;
}
static gpr_slice default_pem_root_certs;
static void init_default_pem_root_certs(void) {
default_pem_root_certs = compute_default_pem_root_certs_once();
}
gpr_slice grpc_get_default_ssl_roots_for_testing(void) {
return compute_default_pem_root_certs_once();
} }
size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs) { size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs) {
......
...@@ -209,6 +209,9 @@ grpc_security_status grpc_ssl_channel_security_connector_create( ...@@ -209,6 +209,9 @@ grpc_security_status grpc_ssl_channel_security_connector_create(
/* Gets the default ssl roots. */ /* Gets the default ssl roots. */
size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs); size_t grpc_get_default_ssl_roots(const unsigned char **pem_root_certs);
/* Exposed for TESTING ONLY!. */
gpr_slice grpc_get_default_ssl_roots_for_testing(void);
/* Config for ssl servers. */ /* Config for ssl servers. */
typedef struct { typedef struct {
unsigned char **pem_private_keys; unsigned char **pem_private_keys;
......
/* /*
* *
* Copyright 2015, Google Inc. * Copyright 2015-2016, Google Inc.
* All rights reserved. * All rights reserved.
* *
* Redistribution and use in source and binary forms, with or without * Redistribution and use in source and binary forms, with or without
...@@ -36,6 +36,9 @@ ...@@ -36,6 +36,9 @@
#include "src/core/security/security_connector.h" #include "src/core/security/security_connector.h"
#include "src/core/security/security_context.h" #include "src/core/security/security_context.h"
#include "src/core/support/env.h"
#include "src/core/support/file.h"
#include "src/core/support/string.h"
#include "src/core/tsi/ssl_transport_security.h" #include "src/core/tsi/ssl_transport_security.h"
#include "src/core/tsi/transport_security.h" #include "src/core/tsi/transport_security.h"
#include "test/core/util/test_config.h" #include "test/core/util/test_config.h"
...@@ -44,6 +47,7 @@ ...@@ -44,6 +47,7 @@
#include <grpc/support/alloc.h> #include <grpc/support/alloc.h>
#include <grpc/support/log.h> #include <grpc/support/log.h>
#include <grpc/support/string_util.h>
#include <grpc/support/useful.h> #include <grpc/support/useful.h>
static int check_transport_security_type(const grpc_auth_context *ctx) { static int check_transport_security_type(const grpc_auth_context *ctx) {
...@@ -297,7 +301,66 @@ static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context( ...@@ -297,7 +301,66 @@ static void test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context(
GRPC_AUTH_CONTEXT_UNREF(ctx, "test"); GRPC_AUTH_CONTEXT_UNREF(ctx, "test");
} }
/* TODO(jboeuf): Unit-test tsi_shallow_peer_from_auth_context. */ static const char *roots_for_override_api = "roots for override api";
static grpc_ssl_roots_override_result override_roots_success(
char **pem_root_certs) {
*pem_root_certs = gpr_strdup(roots_for_override_api);
return GRPC_SSL_ROOTS_OVERRIDE_OK;
}
static grpc_ssl_roots_override_result override_roots_permanent_failure(
char **pem_root_certs) {
return GRPC_SSL_ROOTS_OVERRIDE_FAIL_PERMANENTLY;
}
static void test_default_ssl_roots(void) {
const char *roots_for_env_var = "roots for env var";
char *roots_env_var_file_path;
FILE *roots_env_var_file =
gpr_tmpfile("test_roots_for_env_var", &roots_env_var_file_path);
fwrite(roots_for_env_var, 1, strlen(roots_for_env_var), roots_env_var_file);
fclose(roots_env_var_file);
/* First let's get the root through the override: set the env to an invalid
value. */
gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, "");
grpc_set_ssl_roots_override_callback(override_roots_success);
gpr_slice roots = grpc_get_default_ssl_roots_for_testing();
char *roots_contents = gpr_dump_slice(roots, GPR_DUMP_ASCII);
gpr_slice_unref(roots);
GPR_ASSERT(strcmp(roots_contents, roots_for_override_api) == 0);
gpr_free(roots_contents);
/* Now let's set the env var: We should get the contents pointed value
instead. */
gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, roots_env_var_file_path);
roots = grpc_get_default_ssl_roots_for_testing();
roots_contents = gpr_dump_slice(roots, GPR_DUMP_ASCII);
gpr_slice_unref(roots);
GPR_ASSERT(strcmp(roots_contents, roots_for_env_var) == 0);
gpr_free(roots_contents);
/* Now reset the env var. We should fall back to the value overridden using
the api. */
gpr_setenv(GRPC_DEFAULT_SSL_ROOTS_FILE_PATH_ENV_VAR, "");
roots = grpc_get_default_ssl_roots_for_testing();
roots_contents = gpr_dump_slice(roots, GPR_DUMP_ASCII);
gpr_slice_unref(roots);
GPR_ASSERT(strcmp(roots_contents, roots_for_override_api) == 0);
gpr_free(roots_contents);
/* Now setup a permanent failure for the overridden roots and we should get
an empty slice. */
grpc_set_ssl_roots_override_callback(override_roots_permanent_failure);
roots = grpc_get_default_ssl_roots_for_testing();
GPR_ASSERT(GPR_SLICE_IS_EMPTY(roots));
/* Cleanup. */
remove(roots_env_var_file_path);
gpr_free(roots_env_var_file_path);
}
int main(int argc, char **argv) { int main(int argc, char **argv) {
grpc_test_init(argc, argv); grpc_test_init(argc, argv);
...@@ -308,6 +371,7 @@ int main(int argc, char **argv) { ...@@ -308,6 +371,7 @@ int main(int argc, char **argv) {
test_cn_and_one_san_ssl_peer_to_auth_context(); test_cn_and_one_san_ssl_peer_to_auth_context();
test_cn_and_multiple_sans_ssl_peer_to_auth_context(); test_cn_and_multiple_sans_ssl_peer_to_auth_context();
test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context(); test_cn_and_multiple_sans_and_others_ssl_peer_to_auth_context();
test_default_ssl_roots();
grpc_shutdown(); grpc_shutdown();
return 0; return 0;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment