diff options
author | Michael Steinert <mike.steinert@gmail.com> | 2014-03-04 10:10:15 -0700 |
---|---|---|
committer | Michael Steinert <mike.steinert@gmail.com> | 2014-03-04 10:10:15 -0700 |
commit | a5669298f9c87fed00235aefea02d8b84b455570 (patch) | |
tree | fa1b124d1c8c890ccdbbaa799f9a88e9459beba3 /librabbitmq | |
parent | b852f846c46a3bb9451f99844edfbea41f3289f1 (diff) | |
download | rabbitmq-c-github-ask-a5669298f9c87fed00235aefea02d8b84b455570.tar.gz |
[openssl] Support wildcard hostname verification
Most of this code comes from version Curl 7.35.
Diffstat (limited to 'librabbitmq')
-rw-r--r-- | librabbitmq/CMakeLists.txt | 6 | ||||
-rw-r--r-- | librabbitmq/amqp_hostcheck.c | 201 | ||||
-rw-r--r-- | librabbitmq/amqp_hostcheck.h | 36 | ||||
-rw-r--r-- | librabbitmq/amqp_openssl.c | 9 |
4 files changed, 244 insertions, 8 deletions
diff --git a/librabbitmq/CMakeLists.txt b/librabbitmq/CMakeLists.txt index 4568781..d3623b3 100644 --- a/librabbitmq/CMakeLists.txt +++ b/librabbitmq/CMakeLists.txt @@ -81,7 +81,11 @@ if (ENABLE_SSL_SUPPORT) set(AMQP_SSL_SOCKET_H_PATH amqp_ssl_socket.h) if (SSL_ENGINE STREQUAL "OpenSSL") - set(AMQP_SSL_SRCS ${AMQP_SSL_SOCKET_H_PATH} amqp_openssl.c) + set(AMQP_SSL_SRCS ${AMQP_SSL_SOCKET_H_PATH} + amqp_openssl.c + amqp_hostcheck.c + amqp_hostcheck.h + ) include_directories(${OPENSSL_INCLUDE_DIR}) set(AMQP_SSL_LIBS ${OPENSSL_LIBRARIES}) diff --git a/librabbitmq/amqp_hostcheck.c b/librabbitmq/amqp_hostcheck.c new file mode 100644 index 0000000..8ad2cf7 --- /dev/null +++ b/librabbitmq/amqp_hostcheck.c @@ -0,0 +1,201 @@ +/* vim:set ft=c ts=2 sw=2 sts=2 et cindent: */ +/* + * Copyright 1996-2014 Daniel Stenberg <daniel@haxx.se>. + * Copyright 2014 Michael Steinert + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +#include "amqp_private.h" + +#include <string.h> + +/* Portable, consistent toupper (remember EBCDIC). Do not use toupper() + * because its behavior is altered by the current locale. + */ + +static char +amqp_raw_toupper(char in) +{ + switch (in) { + case 'a': + return 'A'; + case 'b': + return 'B'; + case 'c': + return 'C'; + case 'd': + return 'D'; + case 'e': + return 'E'; + case 'f': + return 'F'; + case 'g': + return 'G'; + case 'h': + return 'H'; + case 'i': + return 'I'; + case 'j': + return 'J'; + case 'k': + return 'K'; + case 'l': + return 'L'; + case 'm': + return 'M'; + case 'n': + return 'N'; + case 'o': + return 'O'; + case 'p': + return 'P'; + case 'q': + return 'Q'; + case 'r': + return 'R'; + case 's': + return 'S'; + case 't': + return 'T'; + case 'u': + return 'U'; + case 'v': + return 'V'; + case 'w': + return 'W'; + case 'x': + return 'X'; + case 'y': + return 'Y'; + case 'z': + return 'Z'; + } + return in; +} + +/* + * amqp_raw_equal() is for doing "raw" case insensitive strings. This is meant + * to be locale independent and only compare strings we know are safe for + * this. See http://daniel.haxx.se/blog/2008/10/15/strcasecmp-in-turkish/ for + * some further explanation to why this function is necessary. + * + * The function is capable of comparing a-z case insensitively even for + * non-ascii. + */ + +static int +amqp_raw_equal(const char *first, const char *second) +{ + while (*first && *second) { + if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) { + /* get out of the loop as soon as they don't match */ + break; + } + first++; + second++; + } + /* we do the comparison here (possibly again), just to make sure that if + * the loop above is skipped because one of the strings reached zero, we + * must not return this as a successful match + */ + return (amqp_raw_toupper(*first) == amqp_raw_toupper(*second)); +} + +static int +amqp_raw_nequal(const char *first, const char *second, size_t max) +{ + while (*first && *second && max) { + if (amqp_raw_toupper(*first) != amqp_raw_toupper(*second)) { + break; + } + max--; + first++; + second++; + } + if (0 == max) { + return 1; /* they are equal this far */ + } + return amqp_raw_toupper(*first) == amqp_raw_toupper(*second); +} + +/* + * Match a hostname against a wildcard pattern. + * E.g. + * "foo.host.com" matches "*.host.com". + * + * We use the matching rule described in RFC6125, section 6.4.3. + * http://tools.ietf.org/html/rfc6125#section-6.4.3 + */ + +static int +amqp_hostmatch(const char *hostname, const char *pattern) +{ + const char *pattern_label_end, *pattern_wildcard, *hostname_label_end; + int wildcard_enabled; + size_t prefixlen, suffixlen; + pattern_wildcard = strchr(pattern, '*'); + if (pattern_wildcard == NULL) { + return amqp_raw_equal(pattern, hostname) ? 1 : 0; + } + /* We require at least 2 dots in pattern to avoid too wide wildcard match. */ + wildcard_enabled = 1; + pattern_label_end = strchr(pattern, '.'); + if (pattern_label_end == NULL || + strchr(pattern_label_end + 1, '.') == NULL || + pattern_wildcard > pattern_label_end || + amqp_raw_nequal(pattern, "xn--", 4)) { + wildcard_enabled = 0; + } + if (!wildcard_enabled) { + return amqp_raw_equal(pattern, hostname) ? 1 : 0; + } + hostname_label_end = strchr(hostname, '.'); + if (hostname_label_end == NULL || + !amqp_raw_equal(pattern_label_end, hostname_label_end)) { + return 0; + } + /* The wildcard must match at least one character, so the left-most + * label of the hostname is at least as large as the left-most label + * of the pattern. + */ + if (hostname_label_end - hostname < pattern_label_end - pattern) { + return 0; + } + prefixlen = pattern_wildcard - pattern; + suffixlen = pattern_label_end - (pattern_wildcard + 1); + return amqp_raw_nequal(pattern, hostname, prefixlen) && + amqp_raw_nequal(pattern_wildcard + 1, hostname_label_end - suffixlen, + suffixlen) ? 1 : 0; +} + +int +amqp_hostcheck(const char *match_pattern, const char *hostname) +{ + /* sanity check */ + if (!match_pattern || !*match_pattern || !hostname || !*hostname) { + return 0; + } + /* trivial case */ + if (amqp_raw_equal(hostname, match_pattern)) { + return 1; + } + return amqp_hostmatch(hostname, match_pattern); +} diff --git a/librabbitmq/amqp_hostcheck.h b/librabbitmq/amqp_hostcheck.h new file mode 100644 index 0000000..2832933 --- /dev/null +++ b/librabbitmq/amqp_hostcheck.h @@ -0,0 +1,36 @@ +/* vim:set ft=c ts=2 sw=2 sts=2 et cindent: */ +#ifndef librabbitmq_amqp_hostcheck_h +#define librabbitmq_amqp_hostcheck_h + +/* + * Copyright 1996-2014 Daniel Stenberg <daniel@haxx.se>. + * Copyright 2014 Michael Steinert + * + * All rights reserved. + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT OF THIRD PARTY RIGHTS. + * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, + * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR + * OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE + * USE OR OTHER DEALINGS IN THE SOFTWARE. + * + * Except as contained in this notice, the name of a copyright holder shall + * not be used in advertising or otherwise to promote the sale, use or other + * dealings in this Software without prior written authorization of the + * copyright holder. + */ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +int +amqp_hostcheck(const char *match_pattern, const char *hostname); + +#endif diff --git a/librabbitmq/amqp_openssl.c b/librabbitmq/amqp_openssl.c index 32595ae..ab8a94e 100644 --- a/librabbitmq/amqp_openssl.c +++ b/librabbitmq/amqp_openssl.c @@ -31,6 +31,7 @@ #include "amqp_ssl_socket.h" #include "amqp_socket.h" +#include "amqp_hostcheck.h" #include "amqp_private.h" #include "threads.h" @@ -214,15 +215,9 @@ amqp_ssl_socket_verify_hostname(void *base, const char *host) goto error; } } -#ifdef _MSC_VER -#define strcasecmp _stricmp -#endif - if (strcasecmp(host, (char *)utf8_value)) { + if (!amqp_hostcheck((char *)utf8_value, host)) { goto error; } -#ifdef _MSC_VER -#undef strcasecmp -#endif exit: OPENSSL_free(utf8_value); return status; |