1 //
2 //
3 // Copyright 2015 gRPC authors.
4 //
5 // Licensed under the Apache License, Version 2.0 (the "License");
6 // you may not use this file except in compliance with the License.
7 // You may obtain a copy of the License at
8 //
9 // http://www.apache.org/licenses/LICENSE-2.0
10 //
11 // Unless required by applicable law or agreed to in writing, software
12 // distributed under the License is distributed on an "AS IS" BASIS,
13 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 // See the License for the specific language governing permissions and
15 // limitations under the License.
16 //
17 //
18
19 #include "src/core/tsi/transport_security.h"
20
21 #include <string.h>
22
23 #include <string>
24
25 #include <gtest/gtest.h>
26 #include <openssl/crypto.h>
27
28 #include "absl/strings/str_format.h"
29
30 #include <grpc/support/alloc.h>
31 #include <grpc/support/log.h>
32 #include <grpc/support/string_util.h>
33
34 #include "src/core/lib/gpr/string.h"
35 #include "src/core/lib/gpr/useful.h"
36 #include "src/core/lib/gprpp/crash.h"
37 #include "src/core/tsi/fake_transport_security.h"
38 #include "src/core/tsi/ssl_transport_security.h"
39 #include "test/core/util/test_config.h"
40
41 typedef struct {
42 // 1 if success, 0 if failure.
43 int expected;
44
45 // Host name to match.
46 const char* host_name;
47
48 // Common name (CN).
49 const char* common_name;
50
51 // Comma separated list of certificate names to match against. Any occurrence
52 // of '#' will be replaced with a null character before processing.
53 const char* dns_names;
54
55 // Comma separated list of IP SANs to match aggainst
56 const char* ip_names;
57 } cert_name_test_entry;
58
59 // Largely inspired from:
60 // chromium/src/net/cert/x509_certificate_unittest.cc.
61 // TODO(jboeuf) uncomment test cases as we fix tsi_ssl_peer_matches_name.
62 const cert_name_test_entry cert_name_test_entries[] = {
63 {1, "foo.com", "foo.com", nullptr, nullptr},
64 {1, "f", "f", nullptr, nullptr},
65 {0, "h", "i", nullptr, nullptr},
66 {1, "bar.foo.com", "*.foo.com", nullptr, nullptr},
67 {1, "www.test.fr", "common.name",
68 "*.test.com,*.test.co.uk,*.test.de,*.test.fr", nullptr},
69 //
70 // {1, "wwW.tESt.fr", "common.name", ",*.*,*.test.de,*.test.FR,www"},
71 //
72 {0, "f.uk", ".uk", nullptr, nullptr},
73 {0, "w.bar.foo.com", "?.bar.foo.com", nullptr, nullptr},
74 {0, "www.foo.com", "(www|ftp).foo.com", nullptr, nullptr},
75 {0, "www.foo.com", "www.foo.com#", nullptr, nullptr}, // # = null char.
76 {0, "www.foo.com", "", "www.foo.com#*.foo.com,#,#", nullptr},
77 {0, "www.house.example", "ww.house.example", nullptr, nullptr},
78 {0, "test.org", "", "www.test.org,*.test.org,*.org", nullptr},
79 {0, "w.bar.foo.com", "w*.bar.foo.com", nullptr, nullptr},
80 {0, "www.bar.foo.com", "ww*ww.bar.foo.com", nullptr, nullptr},
81 {0, "wwww.bar.foo.com", "ww*ww.bar.foo.com", nullptr, nullptr},
82 {0, "wwww.bar.foo.com", "w*w.bar.foo.com", nullptr, nullptr},
83 {0, "wwww.bar.foo.com", "w*w.bar.foo.c0m", nullptr, nullptr},
84 {0, "WALLY.bar.foo.com", "wa*.bar.foo.com", nullptr, nullptr},
85 {0, "wally.bar.foo.com", "*Ly.bar.foo.com", nullptr, nullptr},
86 //
87 // {1, "ww%57.foo.com", "", "www.foo.com"},
88 // {1, "www&.foo.com", "www%26.foo.com", NULL},
89 //
90
91 // Common name must not be used if subject alternative name was provided.
92 {0, "www.test.co.jp", "www.test.co.jp",
93 "*.test.de,*.jp,www.test.co.uk,www.*.co.jp", nullptr},
94 {0, "www.bar.foo.com", "www.bar.foo.com",
95 "*.foo.com,*.*.foo.com,*.*.bar.foo.com,*..bar.foo.com,", nullptr},
96
97 // IDN tests
98 {1, "xn--poema-9qae5a.com.br", "xn--poema-9qae5a.com.br", nullptr, nullptr},
99 {1, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br", nullptr,
100 nullptr},
101 {0, "xn--poema-9qae5a.com.br", "",
102 "*.xn--poema-9qae5a.com.br,"
103 "xn--poema-*.com.br,"
104 "xn--*-9qae5a.com.br,"
105 "*--poema-9qae5a.com.br",
106 nullptr},
107
108 // The following are adapted from the examples quoted from
109 // http://tools.ietf.org/html/rfc6125#section-6.4.3
110 // (e.g., *.example.com would match foo.example.com but
111 // not bar.foo.example.com or example.com).
112 {1, "foo.example.com", "*.example.com", nullptr, nullptr},
113 {0, "bar.foo.example.com", "*.example.com", nullptr, nullptr},
114 {0, "example.com", "*.example.com", nullptr, nullptr},
115
116 // Partial wildcards are disallowed, though RFC 2818 rules allow them.
117 // That is, forms such as baz*.example.net, *baz.example.net, and
118 // b*z.example.net should NOT match domains. Instead, the wildcard must
119 // always be the left-most label, and only a single label.
120 {0, "baz1.example.net", "baz*.example.net", nullptr, nullptr},
121 {0, "foobaz.example.net", "*baz.example.net", nullptr, nullptr},
122 {0, "buzz.example.net", "b*z.example.net", nullptr, nullptr},
123 {0, "www.test.example.net", "www.*.example.net", nullptr, nullptr},
124
125 // Wildcards should not be valid for public registry controlled domains,
126 // and unknown/unrecognized domains, at least three domain components must
127 // be present.
128 {1, "www.test.example", "*.test.example", nullptr, nullptr},
129 {1, "test.example.co.uk", "*.example.co.uk", nullptr, nullptr},
130 {0, "test.example", "*.example", nullptr, nullptr},
131 //
132 // {0, "example.co.uk", "*.co.uk", NULL},
133 //
134 {0, "foo.com", "*.com", nullptr, nullptr},
135 {0, "foo.us", "*.us", nullptr, nullptr},
136 {0, "foo", "*", nullptr, nullptr},
137
138 // IDN variants of wildcards and registry controlled domains.
139 {1, "www.xn--poema-9qae5a.com.br", "*.xn--poema-9qae5a.com.br", nullptr,
140 nullptr},
141 {1, "test.example.xn--mgbaam7a8h", "*.example.xn--mgbaam7a8h", nullptr,
142 nullptr},
143 //
144 // {0, "xn--poema-9qae5a.com.br", "*.com.br", NULL},
145 //
146 {0, "example.xn--mgbaam7a8h", "*.xn--mgbaam7a8h", nullptr, nullptr},
147
148 // Wildcards should be permissible for 'private' registry controlled
149 // domains.
150 {1, "www.appspot.com", "*.appspot.com", nullptr, nullptr},
151 {1, "foo.s3.amazonaws.com", "*.s3.amazonaws.com", nullptr, nullptr},
152
153 // Multiple wildcards are not valid.
154 {0, "foo.example.com", "*.*.com", nullptr, nullptr},
155 {0, "foo.bar.example.com", "*.bar.*.com", nullptr, nullptr},
156
157 // Absolute vs relative DNS name tests. Although not explicitly specified
158 // in RFC 6125, absolute reference names (those ending in a .) should
159 // match either absolute or relative presented names.
160 {1, "foo.com", "foo.com.", nullptr, nullptr},
161 {1, "foo.com.", "foo.com", nullptr, nullptr},
162 {1, "foo.com.", "foo.com.", nullptr, nullptr},
163 {1, "f", "f.", nullptr, nullptr},
164 {1, "f.", "f", nullptr, nullptr},
165 {1, "f.", "f.", nullptr, nullptr},
166 {1, "www-3.bar.foo.com", "*.bar.foo.com.", nullptr, nullptr},
167 {1, "www-3.bar.foo.com.", "*.bar.foo.com", nullptr, nullptr},
168 {1, "www-3.bar.foo.com.", "*.bar.foo.com.", nullptr, nullptr},
169 {0, ".", ".", nullptr, nullptr},
170 {0, "example.com", "*.com.", nullptr, nullptr},
171 {0, "example.com.", "*.com", nullptr, nullptr},
172 {0, "example.com.", "*.com.", nullptr, nullptr},
173 {0, "foo.", "*.", nullptr, nullptr},
174 {0, "foo", "*.", nullptr, nullptr},
175 //
176 // {0, "foo.co.uk", "*.co.uk.", NULL},
177 // {0, "foo.co.uk.", "*.co.uk.", NULL},
178 //
179
180 // An empty CN is OK.
181 {1, "test.foo.com", "", "test.foo.com", nullptr},
182
183 // An IP should not be used for the CN.
184 {0, "173.194.195.139", "173.194.195.139", nullptr, nullptr},
185 // An IP can be used if the SAN IP is present
186 {1, "173.194.195.139", "foo.example.com", nullptr, "173.194.195.139"},
187 {0, "173.194.195.139", "foo.example.com", nullptr, "8.8.8.8"},
188 {0, "173.194.195.139", "foo.example.com", nullptr, "8.8.8.8,8.8.4.4"},
189 {1, "173.194.195.139", "foo.example.com", nullptr,
190 "8.8.8.8,173.194.195.139"},
191 {0, "173.194.195.139", "foo.example.com", nullptr, "173.194.195.13"},
192 {0, "2001:db8:a0b:12f0::1", "foo.example.com", nullptr, "173.194.195.13"},
193 {1, "2001:db8:a0b:12f0::1", "foo.example.com", nullptr,
194 "2001:db8:a0b:12f0::1"},
195 {0, "2001:db8:a0b:12f0::1", "foo.example.com", nullptr,
196 "2001:db8:a0b:12f0::2"},
197 {1, "2001:db8:a0b:12f0::1", "foo.example.com", nullptr,
198 "2001:db8:a0b:12f0::2,2001:db8:a0b:12f0::1,8.8.8.8"},
199 };
200
201 typedef struct name_list {
202 const char* name;
203 struct name_list* next;
204 } name_list;
205
206 typedef struct {
207 size_t name_count;
208 char* buffer;
209 name_list* names;
210 } parsed_names;
211
name_list_add(const char * n)212 name_list* name_list_add(const char* n) {
213 name_list* result = static_cast<name_list*>(gpr_malloc(sizeof(name_list)));
214 result->name = n;
215 result->next = nullptr;
216 return result;
217 }
218
parse_names(const char * names_str)219 static parsed_names parse_names(const char* names_str) {
220 parsed_names result;
221 name_list* current_nl;
222 size_t i;
223 memset(&result, 0, sizeof(parsed_names));
224 if (names_str == nullptr) return result;
225 result.name_count = 1;
226 result.buffer = gpr_strdup(names_str);
227 result.names = name_list_add(result.buffer);
228 current_nl = result.names;
229 for (i = 0; i < strlen(names_str); i++) {
230 if (names_str[i] == ',') {
231 result.buffer[i] = '\0';
232 result.name_count++;
233 i++;
234 current_nl->next = name_list_add(result.buffer + i);
235 current_nl = current_nl->next;
236 }
237 }
238 return result;
239 }
240
destruct_parsed_names(parsed_names * pdn)241 static void destruct_parsed_names(parsed_names* pdn) {
242 name_list* nl = pdn->names;
243 if (pdn->buffer != nullptr) gpr_free(pdn->buffer);
244 while (nl != nullptr) {
245 name_list* to_be_free = nl;
246 nl = nl->next;
247 gpr_free(to_be_free);
248 }
249 }
250
processed_name(const char * name)251 static char* processed_name(const char* name) {
252 char* result = gpr_strdup(name);
253 size_t i;
254 for (i = 0; i < strlen(result); i++) {
255 if (result[i] == '#') {
256 result[i] = '\0';
257 }
258 }
259 return result;
260 }
261
peer_from_cert_name_test_entry(const cert_name_test_entry * entry)262 static tsi_peer peer_from_cert_name_test_entry(
263 const cert_name_test_entry* entry) {
264 size_t i;
265 tsi_peer peer;
266 name_list* nl;
267 parsed_names dns_entries = parse_names(entry->dns_names);
268 parsed_names ip_entries = parse_names(entry->ip_names);
269 nl = dns_entries.names;
270 EXPECT_EQ(tsi_construct_peer(
271 1 + dns_entries.name_count + ip_entries.name_count, &peer),
272 TSI_OK);
273 EXPECT_EQ(tsi_construct_string_peer_property_from_cstring(
274 TSI_X509_SUBJECT_COMMON_NAME_PEER_PROPERTY, entry->common_name,
275 &peer.properties[0]),
276 TSI_OK);
277 i = 1;
278 while (nl != nullptr) {
279 char* processed = processed_name(nl->name);
280 EXPECT_EQ(tsi_construct_string_peer_property(
281 TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, processed,
282 strlen(nl->name), &peer.properties[i++]),
283 TSI_OK);
284 nl = nl->next;
285 gpr_free(processed);
286 }
287
288 nl = ip_entries.names;
289 while (nl != nullptr) {
290 char* processed = processed_name(nl->name);
291 EXPECT_EQ(tsi_construct_string_peer_property(
292 TSI_X509_SUBJECT_ALTERNATIVE_NAME_PEER_PROPERTY, processed,
293 strlen(nl->name), &peer.properties[i++]),
294 TSI_OK);
295 nl = nl->next;
296 gpr_free(processed);
297 }
298 destruct_parsed_names(&dns_entries);
299 destruct_parsed_names(&ip_entries);
300 return peer;
301 }
302
cert_name_test_entry_to_string(const cert_name_test_entry * entry)303 std::string cert_name_test_entry_to_string(const cert_name_test_entry* entry) {
304 return absl::StrFormat(
305 "{ success = %s, host_name = %s, common_name = %s, dns_names = "
306 "%s, ip_names = %s}",
307 entry->expected ? "true" : "false", entry->host_name, entry->common_name,
308 entry->dns_names != nullptr ? entry->dns_names : "",
309 entry->ip_names != nullptr ? entry->ip_names : "");
310 }
311
TEST(TransportSecurityTest,TestPeerMatchesName)312 TEST(TransportSecurityTest, TestPeerMatchesName) {
313 size_t i = 0;
314 for (i = 0; i < GPR_ARRAY_SIZE(cert_name_test_entries); i++) {
315 const cert_name_test_entry* entry = &cert_name_test_entries[i];
316 tsi_peer peer = peer_from_cert_name_test_entry(entry);
317 int result = tsi_ssl_peer_matches_name(&peer, entry->host_name);
318 if (result != entry->expected) {
319 gpr_log(GPR_ERROR, "%s", cert_name_test_entry_to_string(entry).c_str());
320 ASSERT_TRUE(0); // Unexpected result.
321 }
322 tsi_peer_destruct(&peer);
323 }
324 }
325
326 typedef struct {
327 tsi_result res;
328 const char* str;
329 } tsi_result_string_pair;
330
TEST(TransportSecurityTest,TestResultStrings)331 TEST(TransportSecurityTest, TestResultStrings) {
332 const tsi_result_string_pair results[] = {
333 {TSI_OK, "TSI_OK"},
334 {TSI_UNKNOWN_ERROR, "TSI_UNKNOWN_ERROR"},
335 {TSI_INVALID_ARGUMENT, "TSI_INVALID_ARGUMENT"},
336 {TSI_PERMISSION_DENIED, "TSI_PERMISSION_DENIED"},
337 {TSI_INCOMPLETE_DATA, "TSI_INCOMPLETE_DATA"},
338 {TSI_FAILED_PRECONDITION, "TSI_FAILED_PRECONDITION"},
339 {TSI_UNIMPLEMENTED, "TSI_UNIMPLEMENTED"},
340 {TSI_INTERNAL_ERROR, "TSI_INTERNAL_ERROR"},
341 {TSI_DATA_CORRUPTED, "TSI_DATA_CORRUPTED"},
342 {TSI_NOT_FOUND, "TSI_NOT_FOUND"},
343 {TSI_PROTOCOL_FAILURE, "TSI_PROTOCOL_FAILURE"},
344 {TSI_HANDSHAKE_IN_PROGRESS, "TSI_HANDSHAKE_IN_PROGRESS"},
345 {TSI_OUT_OF_RESOURCES, "TSI_OUT_OF_RESOURCES"}};
346 size_t i;
347 for (i = 0; i < GPR_ARRAY_SIZE(results); i++) {
348 ASSERT_STREQ(results[i].str, tsi_result_to_string(results[i].res));
349 }
350 ASSERT_STREQ("UNKNOWN", tsi_result_to_string((tsi_result)42));
351 }
352
TEST(TransportSecurityTest,TestProtectorInvalidArgs)353 TEST(TransportSecurityTest, TestProtectorInvalidArgs) {
354 ASSERT_EQ(
355 tsi_frame_protector_protect(nullptr, nullptr, nullptr, nullptr, nullptr),
356 TSI_INVALID_ARGUMENT);
357 ASSERT_EQ(
358 tsi_frame_protector_protect_flush(nullptr, nullptr, nullptr, nullptr),
359 TSI_INVALID_ARGUMENT);
360 ASSERT_EQ(tsi_frame_protector_unprotect(nullptr, nullptr, nullptr, nullptr,
361 nullptr),
362 TSI_INVALID_ARGUMENT);
363 }
364
TEST(TransportSecurityTest,TestHandshakerInvalidArgs)365 TEST(TransportSecurityTest, TestHandshakerInvalidArgs) {
366 ASSERT_EQ(tsi_handshaker_get_result(nullptr), TSI_INVALID_ARGUMENT);
367 ASSERT_EQ(tsi_handshaker_extract_peer(nullptr, nullptr),
368 TSI_INVALID_ARGUMENT);
369 ASSERT_EQ(tsi_handshaker_create_frame_protector(nullptr, nullptr, nullptr),
370 TSI_INVALID_ARGUMENT);
371 ASSERT_EQ(tsi_handshaker_process_bytes_from_peer(nullptr, nullptr, nullptr),
372 TSI_INVALID_ARGUMENT);
373 ASSERT_EQ(tsi_handshaker_get_bytes_to_send_to_peer(nullptr, nullptr, nullptr),
374 TSI_INVALID_ARGUMENT);
375 ASSERT_EQ(tsi_handshaker_next(nullptr, nullptr, 0, nullptr, nullptr, nullptr,
376 nullptr, nullptr),
377 TSI_INVALID_ARGUMENT);
378 }
379
TEST(TransportSecurityTest,TestHandshakerInvalidState)380 TEST(TransportSecurityTest, TestHandshakerInvalidState) {
381 tsi_handshaker* h = tsi_create_fake_handshaker(0);
382 tsi_peer peer;
383 tsi_frame_protector* p;
384 ASSERT_EQ(tsi_handshaker_extract_peer(h, &peer), TSI_FAILED_PRECONDITION);
385 ASSERT_EQ(tsi_handshaker_create_frame_protector(h, nullptr, &p),
386 TSI_FAILED_PRECONDITION);
387 tsi_handshaker_destroy(h);
388 }
389
main(int argc,char ** argv)390 int main(int argc, char** argv) {
391 grpc::testing::TestEnvironment env(&argc, argv);
392 ::testing::InitGoogleTest(&argc, argv);
393 return RUN_ALL_TESTS();
394 }
395