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