xref: /aosp_15_r20/external/grpc-grpc/test/core/tsi/transport_security_test.cc (revision cc02d7e222339f7a4f6ba5f422e6413f4bd931f2)
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