| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366 | /* * * 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. * */#include "src/core/tsi/transport_security.h"#include <string.h>#include <grpc/support/alloc.h>#include <grpc/support/log.h>#include <grpc/support/string_util.h>#include <grpc/support/useful.h>#include <openssl/crypto.h>#include "src/core/support/string.h"#include "src/core/tsi/fake_transport_security.h"#include "src/core/tsi/ssl_transport_security.h"#include "test/core/util/test_config.h"typedef struct {  /* 1 if success, 0 if failure. */  int expected;  /* Host name to match. */  const char *host_name;  /* Common name (CN). */  const char *common_name;  /* Comma separated list of certificate names to match against. Any occurrence     of '#' will be replaced with a null character before processing. */  const char *dns_names;} cert_name_test_entry;/* Largely inspired from:   chromium/src/net/cert/x509_certificate_unittest.cc.   TODO(jboeuf) uncomment test cases as we fix tsi_ssl_peer_matches_name. */const cert_name_test_entry cert_name_test_entries[] = {    {1, "foo.com", "foo.com", NULL},    {1, "f", "f", NULL},    {0, "h", "i", NULL},    {1, "bar.foo.com", "*.foo.com", NULL},    {1, "www.test.fr", "common.name",     "*.test.com,*.test.co.uk,*.test.de,*.test.fr"},    /*       {1, "wwW.tESt.fr", "common.name", ",*.*,*.test.de,*.test.FR,www"},     */    {0, "f.uk", ".uk", NULL},    {0, "w.bar.foo.com", "?.bar.foo.com", NULL},    {0, "www.foo.com", "(www|ftp).foo.com", NULL},    {0, "www.foo.com", "www.foo.com#", NULL}, /* # = null char. */    {0, "www.foo.com", "", "www.foo.com#*.foo.com,#,#"},    {0, "www.house.example", "ww.house.example", NULL},    {0, "test.org", "", "www.test.org,*.test.org,*.org"},    {0, "w.bar.foo.com", "w*.bar.foo.com", NULL},    {0, "www.bar.foo.com", "ww*ww.bar.foo.com", NULL},    {0, "wwww.bar.foo.com", "ww*ww.bar.foo.com", NULL},    {0, "wwww.bar.foo.com", "w*w.bar.foo.com", NULL},    {0, "wwww.bar.foo.com", "w*w.bar.foo.c0m", NULL},    {0, "WALLY.bar.foo.com", "wa*.bar.foo.com", NULL},    {0, "wally.bar.foo.com", "*Ly.bar.foo.com", NULL},    /*       {1, "ww%57.foo.com", "", "www.foo.com"},       {1, "www&.foo.com", "www%26.foo.com", NULL},     */    /* Common name must not be used if subject alternative name was provided. */    {0, "www.test.co.jp", "www.test.co.jp",     "*.test.de,*.jp,www.test.co.uk,www.*.co.jp"},    {0, "www.bar.foo.com", "www.bar.foo.com",     "*.foo.com,*.*.foo.com,*.*.bar.foo.com,*..bar.foo.com,"},    /* IDN tests */    {1, "xn--poema-9qae5a.com.br", "xn--poema-9qae5a.com.br", NULL},    {1, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br", NULL},    {0, "xn--poema-9qae5a.com.br", "",     "*.xn--poema-9qae5a.com.br,"     "xn--poema-*.com.br,"     "xn--*-9qae5a.com.br,"     "*--poema-9qae5a.com.br"},    /* The following are adapted from the  examples quoted from       http://tools.ietf.org/html/rfc6125#section-6.4.3       (e.g., *.example.com would match foo.example.com but       not bar.foo.example.com or example.com). */    {1, "foo.example.com", "*.example.com", NULL},    {0, "bar.foo.example.com", "*.example.com", NULL},    {0, "example.com", "*.example.com", NULL},    /* Partial wildcards are disallowed, though RFC 2818 rules allow them.       That is, forms such as baz*.example.net, *baz.example.net, and       b*z.example.net should NOT match domains. Instead, the wildcard must       always be the left-most label, and only a single label. */    {0, "baz1.example.net", "baz*.example.net", NULL},    {0, "foobaz.example.net", "*baz.example.net", NULL},    {0, "buzz.example.net", "b*z.example.net", NULL},    {0, "www.test.example.net", "www.*.example.net", NULL},    /* Wildcards should not be valid for public registry controlled domains,       and unknown/unrecognized domains, at least three domain components must       be present. */    {1, "www.test.example", "*.test.example", NULL},    {1, "test.example.co.uk", "*.example.co.uk", NULL},    {0, "test.example", "*.example", NULL},    /*       {0, "example.co.uk", "*.co.uk", NULL},     */    {0, "foo.com", "*.com", NULL},    {0, "foo.us", "*.us", NULL},    {0, "foo", "*", NULL},    /* IDN variants of wildcards and registry controlled domains. */    {1, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br", NULL},    {1, "test.example.xn--mgbaam7a8h", "*.example.xn--mgbaam7a8h", NULL},    /*       {0, "xn--poema-9qae5a.com.br", "*.com.br", NULL},     */    {0, "example.xn--mgbaam7a8h", "*.xn--mgbaam7a8h", NULL},    /* Wildcards should be permissible for 'private' registry controlled       domains. */    {1, "www.appspot.com", "*.appspot.com", NULL},    {1, "foo.s3.amazonaws.com", "*.s3.amazonaws.com", NULL},    /* Multiple wildcards are not valid. */    {0, "foo.example.com", "*.*.com", NULL},    {0, "foo.bar.example.com", "*.bar.*.com", NULL},    /* Absolute vs relative DNS name tests. Although not explicitly specified       in RFC 6125, absolute reference names (those ending in a .) should       match either absolute or relative presented names. */    {1, "foo.com", "foo.com.", NULL},    {1, "foo.com.", "foo.com", NULL},    {1, "foo.com.", "foo.com.", NULL},    {1, "f", "f.", NULL},    {1, "f.", "f", NULL},    {1, "f.", "f.", NULL},    {1, "www-3.bar.foo.com", "*.bar.foo.com.", NULL},    {1, "www-3.bar.foo.com.", "*.bar.foo.com", NULL},    {1, "www-3.bar.foo.com.", "*.bar.foo.com.", NULL},    {0, ".", ".", NULL},    {0, "example.com", "*.com.", NULL},    {0, "example.com.", "*.com", NULL},    {0, "example.com.", "*.com.", NULL},    {0, "foo.", "*.", NULL},    {0, "foo", "*.", NULL},    /*       {0, "foo.co.uk", "*.co.uk.", NULL},       {0, "foo.co.uk.", "*.co.uk.", NULL},     */    /* An empty CN is OK. */    {1, "test.foo.com", "", "test.foo.com"},    /* An IP should not be used for the CN. */    {0, "173.194.195.139", "173.194.195.139", NULL},};typedef struct name_list {  const char *name;  struct name_list *next;} name_list;typedef struct {  size_t name_count;  char *buffer;  name_list *names;} parsed_dns_names;name_list *name_list_add(const char *n) {  name_list *result = gpr_malloc(sizeof(name_list));  result->name = n;  result->next = NULL;  return result;}static parsed_dns_names parse_dns_names(const char *dns_names_str) {  parsed_dns_names result;  name_list *current_nl;  size_t i;  memset(&result, 0, sizeof(parsed_dns_names));  if (dns_names_str == 0) return result;  result.name_count = 1;  result.buffer = gpr_strdup(dns_names_str);  result.names = name_list_add(result.buffer);  current_nl = result.names;  for (i = 0; i < strlen(dns_names_str); i++) {    if (dns_names_str[i] == ',') {      result.buffer[i] = '\0';      result.name_count++;      i++;      current_nl->next = name_list_add(result.buffer + i);      current_nl = current_nl->next;    }  }  return result;}static void destruct_parsed_dns_names(parsed_dns_names *pdn) {  name_list *nl = pdn->names;  if (pdn->buffer != NULL) gpr_free(pdn->buffer);  while (nl != NULL) {    name_list *to_be_free = nl;    nl = nl->next;    gpr_free(to_be_free);  }}static char *processed_dns_name(const char *dns_name) {  char *result = gpr_strdup(dns_name);  size_t i;  for (i = 0; i < strlen(result); i++) {    if (result[i] == '#') {      result[i] = '\0';    }  }  return result;}static tsi_peer peer_from_cert_name_test_entry(    const cert_name_test_entry *entry) {  size_t i;  tsi_peer peer;  name_list *nl;  parsed_dns_names dns_entries = parse_dns_names(entry->dns_names);  nl = dns_entries.names;  GPR_ASSERT(tsi_construct_peer(1 + dns_entries.name_count, &peer) == TSI_OK);  GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(                 TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, entry->common_name,                 &peer.properties[0]) == TSI_OK);  i = 1;  while (nl != NULL) {    char *processed = processed_dns_name(nl->name);    GPR_ASSERT(tsi_construct_string_peer_property(                   TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, processed,                   strlen(nl->name), &peer.properties[i++]) == TSI_OK);    nl = nl->next;    gpr_free(processed);  }  destruct_parsed_dns_names(&dns_entries);  return peer;}char *cert_name_test_entry_to_string(const cert_name_test_entry *entry) {  char *s;  gpr_asprintf(      &s, "{ success = %s, host_name = %s, common_name = %s, dns_names = %s}",      entry->expected ? "true" : "false", entry->host_name, entry->common_name,      entry->dns_names != NULL ? entry->dns_names : "");  return s;}static void test_peer_matches_name(void) {  size_t i = 0;  for (i = 0; i < GPR_ARRAY_SIZE(cert_name_test_entries); i++) {    const cert_name_test_entry *entry = &cert_name_test_entries[i];    tsi_peer peer = peer_from_cert_name_test_entry(entry);    int result = tsi_ssl_peer_matches_name(&peer, entry->host_name);    if (result != entry->expected) {      char *entry_str = cert_name_test_entry_to_string(entry);      gpr_log(GPR_ERROR, "%s", entry_str);      gpr_free(entry_str);      GPR_ASSERT(0); /* Unexpected result. */    }    tsi_peer_destruct(&peer);  }}typedef struct {  tsi_result res;  const char *str;} tsi_result_string_pair;static void test_result_strings(void) {  const tsi_result_string_pair results[] = {      {TSI_OK, "TSI_OK"},      {TSI_UNKNOWN_ERROR, "TSI_UNKNOWN_ERROR"},      {TSI_INVALID_ARGUMENT, "TSI_INVALID_ARGUMENT"},      {TSI_PERMISSION_DENIED, "TSI_PERMISSION_DENIED"},      {TSI_INCOMPLETE_DATA, "TSI_INCOMPLETE_DATA"},      {TSI_FAILED_PRECONDITION, "TSI_FAILED_PRECONDITION"},      {TSI_UNIMPLEMENTED, "TSI_UNIMPLEMENTED"},      {TSI_INTERNAL_ERROR, "TSI_INTERNAL_ERROR"},      {TSI_DATA_CORRUPTED, "TSI_DATA_CORRUPTED"},      {TSI_NOT_FOUND, "TSI_NOT_FOUND"},      {TSI_PROTOCOL_FAILURE, "TSI_PROTOCOL_FAILURE"},      {TSI_HANDSHAKE_IN_PROGRESS, "TSI_HANDSHAKE_IN_PROGRESS"},      {TSI_OUT_OF_RESOURCES, "TSI_OUT_OF_RESOURCES"}};  size_t i;  for (i = 0; i < GPR_ARRAY_SIZE(results); i++) {    GPR_ASSERT(strcmp(results[i].str, tsi_result_to_string(results[i].res)) ==               0);  }  GPR_ASSERT(strcmp("UNKNOWN", tsi_result_to_string((tsi_result)42)) == 0);}static void test_protector_invalid_args(void) {  GPR_ASSERT(tsi_frame_protector_protect(NULL, NULL, NULL, NULL, NULL) ==             TSI_INVALID_ARGUMENT);  GPR_ASSERT(tsi_frame_protector_protect_flush(NULL, NULL, NULL, NULL) ==             TSI_INVALID_ARGUMENT);  GPR_ASSERT(tsi_frame_protector_unprotect(NULL, NULL, NULL, NULL, NULL) ==             TSI_INVALID_ARGUMENT);}static void test_handshaker_invalid_args(void) {  GPR_ASSERT(tsi_handshaker_get_result(NULL) == TSI_INVALID_ARGUMENT);  GPR_ASSERT(tsi_handshaker_extract_peer(NULL, NULL) == TSI_INVALID_ARGUMENT);  GPR_ASSERT(tsi_handshaker_create_frame_protector(NULL, NULL, NULL) ==             TSI_INVALID_ARGUMENT);  GPR_ASSERT(tsi_handshaker_process_bytes_from_peer(NULL, NULL, NULL) ==             TSI_INVALID_ARGUMENT);  GPR_ASSERT(tsi_handshaker_get_bytes_to_send_to_peer(NULL, NULL, NULL) ==             TSI_INVALID_ARGUMENT);}static void test_handshaker_invalid_state(void) {  tsi_handshaker *h = tsi_create_fake_handshaker(0);  tsi_peer peer;  tsi_frame_protector *p;  GPR_ASSERT(tsi_handshaker_extract_peer(h, &peer) == TSI_FAILED_PRECONDITION);  GPR_ASSERT(tsi_handshaker_create_frame_protector(h, NULL, &p) ==             TSI_FAILED_PRECONDITION);  tsi_handshaker_destroy(h);}int main(int argc, char **argv) {  grpc_test_init(argc, argv);  test_peer_matches_name();  test_result_strings();  test_protector_invalid_args();  test_handshaker_invalid_args();  test_handshaker_invalid_state();  return 0;}
 |