diff options
Diffstat (limited to 'src/core/tsi/ssl_transport_security.c')
-rw-r--r-- | src/core/tsi/ssl_transport_security.c | 105 |
1 files changed, 86 insertions, 19 deletions
diff --git a/src/core/tsi/ssl_transport_security.c b/src/core/tsi/ssl_transport_security.c index 85b0922a43..9ca8e6ddc9 100644 --- a/src/core/tsi/ssl_transport_security.c +++ b/src/core/tsi/ssl_transport_security.c @@ -180,6 +180,30 @@ static void ssl_info_callback(const SSL* ssl, int where, int ret) { ssl_log_where_info(ssl, where, SSL_CB_HANDSHAKE_DONE, "HANDSHAKE DONE"); } +/* Returns 1 if name looks like an IP address, 0 otherwise. + This is a very rough heuristic as it does not handle IPV6 or things like: + 0300.0250.00.01, 0xC0.0Xa8.0x0.0x1, 000030052000001, 0xc0.052000001 */ +static int looks_like_ip_address(const char *name) { + size_t i; + size_t dot_count = 0; + size_t num_size = 0; + for (i = 0; i < strlen(name); i++) { + if (name[i] >= '0' && name[i] <= '9') { + if (num_size > 3) return 0; + num_size++; + } else if (name[i] == '.') { + if (dot_count > 3 || num_size == 0) return 0; + dot_count++; + num_size = 0; + } else { + return 0; + } + } + if (dot_count < 3 || num_size == 0) return 0; + return 1; +} + + /* Gets the subject CN from an X509 cert. */ static tsi_result ssl_get_x509_common_name(X509* cert, unsigned char** utf8, size_t* utf8_size) { @@ -226,10 +250,18 @@ static tsi_result peer_property_from_x509_common_name( size_t common_name_size; tsi_result result = ssl_get_x509_common_name(cert, &common_name, &common_name_size); - if (result != TSI_OK) return result; + if (result != TSI_OK) { + if (result == TSI_NOT_FOUND) { + common_name = NULL; + common_name_size = 0; + } else { + return result; + } + } result = tsi_construct_string_peer_property( - TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, (const char*)common_name, - common_name_size, property); + TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, + common_name == NULL ? "" : (const char*)common_name, common_name_size, + property); OPENSSL_free(common_name); return result; } @@ -1036,9 +1068,22 @@ static void ssl_server_handshaker_factory_destroy( static int does_entry_match_name(const char* entry, size_t entry_length, const char* name) { + const char *dot; const char* name_subdomain = NULL; + size_t name_length = strlen(name); + size_t name_subdomain_length; if (entry_length == 0) return 0; - if (!strncmp(name, entry, entry_length) && (strlen(name) == entry_length)) { + + /* Take care of '.' terminations. */ + if (name[name_length - 1] == '.') { + name_length--; + } + if (entry[entry_length - 1] == '.') { + entry_length--; + if (entry_length == 0) return 0; + } + + if ((name_length == entry_length) && !strncmp(name, entry, entry_length)) { return 1; /* Perfect match. */ } if (entry[0] != '*') return 0; @@ -1049,18 +1094,28 @@ static int does_entry_match_name(const char* entry, size_t entry_length, return 0; } name_subdomain = strchr(name, '.'); - if (name_subdomain == NULL || strlen(name_subdomain) < 2) return 0; + name_subdomain_length = strlen(name_subdomain); + if (name_subdomain == NULL || name_subdomain_length < 2) return 0; name_subdomain++; /* Starts after the dot. */ + name_subdomain_length--; entry += 2; /* Remove *. */ entry_length -= 2; - return (!strncmp(entry, name_subdomain, entry_length) && - (strlen(name_subdomain) == entry_length)); + dot = strchr(name_subdomain, '.'); + if ((dot == NULL) || (dot == &name_subdomain[name_subdomain_length - 1])) { + gpr_log(GPR_ERROR, "Invalid toplevel subdomain: %s", name_subdomain); + return 0; + } + if (name_subdomain[name_subdomain_length - 1] == '.') { + name_subdomain_length--; + } + return ((entry_length > 0) && (name_subdomain_length == entry_length) && + !strncmp(entry, name_subdomain, entry_length)); } static int ssl_server_handshaker_factory_servername_callback(SSL* ssl, int* ap, void* arg) { tsi_ssl_server_handshaker_factory* impl = - (tsi_ssl_server_handshaker_factory*)arg; + (tsi_ssl_server_handshaker_factory*)arg; size_t i = 0; const char* servername = SSL_get_servername(ssl, TLSEXT_NAMETYPE_host_name); if (servername == NULL || strlen(servername) == 0) { @@ -1283,17 +1338,13 @@ tsi_result tsi_create_ssl_server_handshaker_factory( int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { size_t i = 0; - const tsi_peer_property* property = tsi_peer_get_property_by_name( - peer, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); - if (property == NULL || property->type != TSI_PEER_PROPERTY_TYPE_STRING) { - gpr_log(GPR_ERROR, "Invalid x509 subject common name property."); - return 0; - } - if (does_entry_match_name(property->value.string.data, - property->value.string.length, name)) { - return 1; - } + size_t san_count = 0; + const tsi_peer_property* property = NULL; + /* For now reject what looks like an IP address. */ + if (looks_like_ip_address(name)) return 0; + + /* Check the SAN first. */ property = tsi_peer_get_property_by_name( peer, TSI_X509_SUBJECT_ALTERNATIVE_NAMES_PEER_PROPERTY); if (property == NULL || property->type != TSI_PEER_PROPERTY_TYPE_LIST) { @@ -1301,7 +1352,8 @@ int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { return 0; } - for (i = 0; i < property->value.list.child_count; i++) { + san_count = property->value.list.child_count; + for (i = 0; i < san_count; i++) { const tsi_peer_property* alt_name_property = &property->value.list.children[i]; if (alt_name_property->type != TSI_PEER_PROPERTY_TYPE_STRING) { @@ -1313,5 +1365,20 @@ int tsi_ssl_peer_matches_name(const tsi_peer* peer, const char* name) { return 1; } } + + /* If there's no SAN, try the CN. */ + if (san_count == 0) { + property = tsi_peer_get_property_by_name( + peer, TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY); + if (property == NULL || property->type != TSI_PEER_PROPERTY_TYPE_STRING) { + gpr_log(GPR_ERROR, "Invalid x509 subject common name property."); + return 0; + } + if (does_entry_match_name(property->value.string.data, + property->value.string.length, name)) { + return 1; + } + } + return 0; /* Not found. */ } |