Line data Source code
1 : /*
2 : *
3 : * Copyright 2015, Google Inc.
4 : * All rights reserved.
5 : *
6 : * Redistribution and use in source and binary forms, with or without
7 : * modification, are permitted provided that the following conditions are
8 : * met:
9 : *
10 : * * Redistributions of source code must retain the above copyright
11 : * notice, this list of conditions and the following disclaimer.
12 : * * Redistributions in binary form must reproduce the above
13 : * copyright notice, this list of conditions and the following disclaimer
14 : * in the documentation and/or other materials provided with the
15 : * distribution.
16 : * * Neither the name of Google Inc. nor the names of its
17 : * contributors may be used to endorse or promote products derived from
18 : * this software without specific prior written permission.
19 : *
20 : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21 : * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22 : * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
23 : * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
24 : * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
25 : * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
26 : * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
27 : * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
28 : * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
29 : * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
30 : * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 : *
32 : */
33 :
34 : #include "src/core/tsi/transport_security.h"
35 :
36 : #include <string.h>
37 :
38 : #include <grpc/support/alloc.h>
39 : #include <grpc/support/log.h>
40 : #include <grpc/support/string_util.h>
41 : #include <grpc/support/useful.h>
42 :
43 : #include <openssl/crypto.h>
44 :
45 : #include "src/core/support/string.h"
46 : #include "src/core/tsi/ssl_transport_security.h"
47 : #include "test/core/util/test_config.h"
48 :
49 : typedef struct {
50 : /* 1 if success, 0 if failure. */
51 : int expected;
52 :
53 : /* Host name to match. */
54 : const char *host_name;
55 :
56 : /* Common name (CN). */
57 : const char *common_name;
58 :
59 : /* Comma separated list of certificate names to match against. Any occurrence
60 : of '#' will be replaced with a null character before processing. */
61 : const char *dns_names;
62 :
63 : } cert_name_test_entry;
64 :
65 : /* Largely inspired from:
66 : chromium/src/net/cert/x509_certificate_unittest.cc.
67 : TODO(jboeuf) uncomment test cases as we fix tsi_ssl_peer_matches_name. */
68 : const cert_name_test_entry cert_name_test_entries[] = {
69 : {1, "foo.com", "foo.com", NULL},
70 : {1, "f", "f", NULL},
71 : {0, "h", "i", NULL},
72 : {1, "bar.foo.com", "*.foo.com", NULL},
73 : {1, "www.test.fr", "common.name",
74 : "*.test.com,*.test.co.uk,*.test.de,*.test.fr"},
75 : /*
76 : {1, "wwW.tESt.fr", "common.name", ",*.*,*.test.de,*.test.FR,www"},
77 : */
78 : {0, "f.uk", ".uk", NULL},
79 : {0, "w.bar.foo.com", "?.bar.foo.com", NULL},
80 : {0, "www.foo.com", "(www|ftp).foo.com", NULL},
81 : {0, "www.foo.com", "www.foo.com#", NULL}, /* # = null char. */
82 : {0, "www.foo.com", "", "www.foo.com#*.foo.com,#,#"},
83 : {0, "www.house.example", "ww.house.example", NULL},
84 : {0, "test.org", "", "www.test.org,*.test.org,*.org"},
85 : {0, "w.bar.foo.com", "w*.bar.foo.com", NULL},
86 : {0, "www.bar.foo.com", "ww*ww.bar.foo.com", NULL},
87 : {0, "wwww.bar.foo.com", "ww*ww.bar.foo.com", NULL},
88 : {0, "wwww.bar.foo.com", "w*w.bar.foo.com", NULL},
89 : {0, "wwww.bar.foo.com", "w*w.bar.foo.c0m", NULL},
90 : {0, "WALLY.bar.foo.com", "wa*.bar.foo.com", NULL},
91 : {0, "wally.bar.foo.com", "*Ly.bar.foo.com", NULL},
92 : /*
93 : {1, "ww%57.foo.com", "", "www.foo.com"},
94 : {1, "www&.foo.com", "www%26.foo.com", NULL},
95 : */
96 :
97 : /* Common name must not be used if subject alternative name was provided. */
98 : {0, "www.test.co.jp", "www.test.co.jp",
99 : "*.test.de,*.jp,www.test.co.uk,www.*.co.jp"},
100 : {0, "www.bar.foo.com", "www.bar.foo.com",
101 : "*.foo.com,*.*.foo.com,*.*.bar.foo.com,*..bar.foo.com,"},
102 :
103 : /* IDN tests */
104 : {1, "xn--poema-9qae5a.com.br", "xn--poema-9qae5a.com.br", NULL},
105 : {1, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br", NULL},
106 : {0, "xn--poema-9qae5a.com.br", "",
107 : "*.xn--poema-9qae5a.com.br,"
108 : "xn--poema-*.com.br,"
109 : "xn--*-9qae5a.com.br,"
110 : "*--poema-9qae5a.com.br"},
111 :
112 : /* The following are adapted from the examples quoted from
113 : http://tools.ietf.org/html/rfc6125#section-6.4.3
114 : (e.g., *.example.com would match foo.example.com but
115 : not bar.foo.example.com or example.com). */
116 : {1, "foo.example.com", "*.example.com", NULL},
117 : {0, "bar.foo.example.com", "*.example.com", NULL},
118 : {0, "example.com", "*.example.com", NULL},
119 :
120 : /* Partial wildcards are disallowed, though RFC 2818 rules allow them.
121 : That is, forms such as baz*.example.net, *baz.example.net, and
122 : b*z.example.net should NOT match domains. Instead, the wildcard must
123 : always be the left-most label, and only a single label. */
124 : {0, "baz1.example.net", "baz*.example.net", NULL},
125 : {0, "foobaz.example.net", "*baz.example.net", NULL},
126 : {0, "buzz.example.net", "b*z.example.net", NULL},
127 : {0, "www.test.example.net", "www.*.example.net", NULL},
128 :
129 : /* Wildcards should not be valid for public registry controlled domains,
130 : and unknown/unrecognized domains, at least three domain components must
131 : be present. */
132 : {1, "www.test.example", "*.test.example", NULL},
133 : {1, "test.example.co.uk", "*.example.co.uk", NULL},
134 : {0, "test.example", "*.example", NULL},
135 : /*
136 : {0, "example.co.uk", "*.co.uk", NULL},
137 : */
138 : {0, "foo.com", "*.com", NULL},
139 : {0, "foo.us", "*.us", NULL},
140 : {0, "foo", "*", NULL},
141 :
142 : /* IDN variants of wildcards and registry controlled domains. */
143 : {1, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br", NULL},
144 : {1, "test.example.xn--mgbaam7a8h", "*.example.xn--mgbaam7a8h", NULL},
145 : /*
146 : {0, "xn--poema-9qae5a.com.br", "*.com.br", NULL},
147 : */
148 : {0, "example.xn--mgbaam7a8h", "*.xn--mgbaam7a8h", NULL},
149 :
150 : /* Wildcards should be permissible for 'private' registry controlled
151 : domains. */
152 : {1, "www.appspot.com", "*.appspot.com", NULL},
153 : {1, "foo.s3.amazonaws.com", "*.s3.amazonaws.com", NULL},
154 :
155 : /* Multiple wildcards are not valid. */
156 : {0, "foo.example.com", "*.*.com", NULL},
157 : {0, "foo.bar.example.com", "*.bar.*.com", NULL},
158 :
159 : /* Absolute vs relative DNS name tests. Although not explicitly specified
160 : in RFC 6125, absolute reference names (those ending in a .) should
161 : match either absolute or relative presented names. */
162 : {1, "foo.com", "foo.com.", NULL},
163 : {1, "foo.com.", "foo.com", NULL},
164 : {1, "foo.com.", "foo.com.", NULL},
165 : {1, "f", "f.", NULL},
166 : {1, "f.", "f", NULL},
167 : {1, "f.", "f.", NULL},
168 : {1, "www-3.bar.foo.com", "*.bar.foo.com.", NULL},
169 : {1, "www-3.bar.foo.com.", "*.bar.foo.com", NULL},
170 : {1, "www-3.bar.foo.com.", "*.bar.foo.com.", NULL},
171 : {0, ".", ".", NULL},
172 : {0, "example.com", "*.com.", NULL},
173 : {0, "example.com.", "*.com", NULL},
174 : {0, "example.com.", "*.com.", NULL},
175 : {0, "foo.", "*.", NULL},
176 : {0, "foo", "*.", NULL},
177 : /*
178 : {0, "foo.co.uk", "*.co.uk.", NULL},
179 : {0, "foo.co.uk.", "*.co.uk.", NULL},
180 : */
181 :
182 : /* An empty CN is OK. */
183 : {1, "test.foo.com", "", "test.foo.com"},
184 :
185 : /* An IP should not be used for the CN. */
186 : {0, "173.194.195.139", "173.194.195.139", NULL},
187 : };
188 :
189 : typedef struct name_list {
190 : const char *name;
191 : struct name_list *next;
192 : } name_list;
193 :
194 : typedef struct {
195 : size_t name_count;
196 : char *buffer;
197 : name_list *names;
198 : } parsed_dns_names;
199 :
200 24 : name_list *name_list_add(const char *n) {
201 24 : name_list *result = gpr_malloc(sizeof(name_list));
202 24 : result->name = n;
203 24 : result->next = NULL;
204 24 : return result;
205 : }
206 :
207 61 : static parsed_dns_names parse_dns_names(const char *dns_names_str) {
208 : parsed_dns_names result;
209 : name_list *current_nl;
210 : size_t i;
211 61 : memset(&result, 0, sizeof(parsed_dns_names));
212 61 : if (dns_names_str == 0) return result;
213 7 : result.name_count = 1;
214 7 : result.buffer = gpr_strdup(dns_names_str);
215 7 : result.names = name_list_add(result.buffer);
216 7 : current_nl = result.names;
217 281 : for (i = 0; i < strlen(dns_names_str); i++) {
218 274 : if (dns_names_str[i] == ',') {
219 17 : result.buffer[i] = '\0';
220 17 : result.name_count++;
221 17 : i++;
222 17 : current_nl->next = name_list_add(result.buffer + i);
223 17 : current_nl = current_nl->next;
224 : }
225 : }
226 7 : return result;
227 : }
228 :
229 61 : static void destruct_parsed_dns_names(parsed_dns_names *pdn) {
230 61 : name_list *nl = pdn->names;
231 61 : if (pdn->buffer != NULL) gpr_free(pdn->buffer);
232 146 : while (nl != NULL) {
233 24 : name_list *to_be_free = nl;
234 24 : nl = nl->next;
235 24 : gpr_free(to_be_free);
236 : }
237 61 : }
238 :
239 24 : static char *processed_dns_name(const char *dns_name) {
240 24 : char *result = gpr_strdup(dns_name);
241 : size_t i;
242 288 : for (i = 0; i < strlen(result); i++) {
243 264 : if (result[i] == '#') {
244 3 : result[i] = '\0';
245 : }
246 : }
247 24 : return result;
248 : }
249 :
250 61 : static tsi_peer peer_from_cert_name_test_entry(
251 : const cert_name_test_entry *entry) {
252 : size_t i;
253 : tsi_peer peer;
254 : name_list *nl;
255 61 : parsed_dns_names dns_entries = parse_dns_names(entry->dns_names);
256 61 : nl = dns_entries.names;
257 61 : GPR_ASSERT(tsi_construct_peer(1 + dns_entries.name_count, &peer) == TSI_OK);
258 61 : GPR_ASSERT(tsi_construct_string_peer_property_from_cstring(
259 : TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, entry->common_name,
260 : &peer.properties[0]) == TSI_OK);
261 61 : i = 1;
262 146 : while (nl != NULL) {
263 24 : char *processed = processed_dns_name(nl->name);
264 24 : GPR_ASSERT(tsi_construct_string_peer_property(
265 : TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, processed,
266 : strlen(nl->name), &peer.properties[i++]) == TSI_OK);
267 24 : nl = nl->next;
268 24 : gpr_free(processed);
269 : }
270 61 : destruct_parsed_dns_names(&dns_entries);
271 61 : return peer;
272 : }
273 :
274 0 : char *cert_name_test_entry_to_string(const cert_name_test_entry *entry) {
275 : char *s;
276 0 : gpr_asprintf(
277 : &s, "{ success = %s, host_name = %s, common_name = %s, dns_names = %s}",
278 0 : entry->expected ? "true" : "false", entry->host_name, entry->common_name,
279 0 : entry->dns_names != NULL ? entry->dns_names : "");
280 0 : return s;
281 : }
282 :
283 1 : static void test_peer_matches_name(void) {
284 1 : size_t i = 0;
285 62 : for (i = 0; i < GPR_ARRAY_SIZE(cert_name_test_entries); i++) {
286 61 : const cert_name_test_entry *entry = &cert_name_test_entries[i];
287 61 : tsi_peer peer = peer_from_cert_name_test_entry(entry);
288 61 : int result = tsi_ssl_peer_matches_name(&peer, entry->host_name);
289 61 : if (result != entry->expected) {
290 0 : char *entry_str = cert_name_test_entry_to_string(entry);
291 0 : gpr_log(GPR_ERROR, "%s", entry_str);
292 0 : gpr_free(entry_str);
293 0 : GPR_ASSERT(0); /* Unexpected result. */
294 : }
295 61 : tsi_peer_destruct(&peer);
296 : }
297 1 : }
298 :
299 1 : int main(int argc, char **argv) {
300 1 : grpc_test_init(argc, argv);
301 1 : test_peer_matches_name();
302 1 : return 0;
303 : }
|