]> git.proxmox.com Git - ceph.git/blob - ceph/src/jaegertracing/thrift/lib/c_glib/test/testtransportsslsocket.c
buildsys: switch source download to quincy
[ceph.git] / ceph / src / jaegertracing / thrift / lib / c_glib / test / testtransportsslsocket.c
1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19 #define _POSIX_C_SOURCE 200112L /* https://stackoverflow.com/questions/37541985/storage-size-of-addrinfo-isnt-known */
20
21
22 #include <sys/wait.h>
23 #include <arpa/inet.h>
24 #include <sys/types.h>
25 #include <sys/socket.h>
26 #include <netdb.h>
27
28 #include <thrift/c_glib/transport/thrift_transport.h>
29 #include <thrift/c_glib/transport/thrift_buffered_transport.h>
30 #include <thrift/c_glib/transport/thrift_server_transport.h>
31 #include <thrift/c_glib/transport/thrift_server_socket.h>
32 #include <thrift/c_glib/transport/thrift_ssl_socket.h>
33
34 /* #define TEST_DATA { 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j' } */
35 #define TEST_DATA { "GET / HTTP/1.1\n\n" }
36
37
38 /* substituted functions to test failures of system and library calls */
39 static int socket_error = 0;
40 int
41 my_socket(int domain, int type, int protocol)
42 {
43 if (socket_error == 0)
44 {
45 return socket (domain, type, protocol);
46 }
47 return -1;
48 }
49
50 static int recv_error = 0;
51 ssize_t
52 my_recv(int socket, void *buffer, size_t length, int flags)
53 {
54 if (recv_error == 0)
55 {
56 return recv (socket, buffer, length, flags);
57 }
58 return -1;
59 }
60
61 static int send_error = 0;
62 ssize_t
63 my_send(int socket, const void *buffer, size_t length, int flags)
64 {
65 if (send_error == 0)
66 {
67 return send (socket, buffer, length, flags);
68 }
69 return -1;
70 }
71
72 #define socket my_socket
73 #define recv my_recv
74 #define send my_send
75 #include "../src/thrift/c_glib/transport/thrift_ssl_socket.c"
76 #undef socket
77 #undef recv
78 #undef send
79
80 static void thrift_socket_server (const int port);
81
82 /* test object creation and destruction */
83 static void
84 test_ssl_create_and_destroy(void)
85 {
86 gchar *hostname = NULL;
87 guint port = 0;
88
89 GObject *object = NULL;
90 object = g_object_new (THRIFT_TYPE_SSL_SOCKET, NULL);
91 g_assert (object != NULL);
92 g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, NULL);
93 g_free (hostname);
94 g_object_unref (object);
95 }
96
97 static void
98 test_ssl_create_and_set_properties(void)
99 {
100 gchar *hostname = NULL;
101 guint port = 0;
102 SSL_CTX* ssl_ctx= NULL;
103 GError *error=NULL;
104
105 GObject *object = NULL;
106 object = thrift_ssl_socket_new(SSLTLS, &error);
107 g_object_get (G_OBJECT(object), "hostname", &hostname, "port", &port, "ssl_context", &ssl_ctx, NULL);
108 g_assert (ssl_ctx!=NULL);
109
110 g_free (hostname);
111 g_object_unref (object);
112 }
113
114 static void
115 test_ssl_open_and_close_non_ssl_server(void)
116 {
117 ThriftSSLSocket *tSSLSocket = NULL;
118 ThriftTransport *transport = NULL;
119 GError *error=NULL;
120 pid_t pid;
121 int non_ssl_port = 51198;
122 char errormsg[255];
123
124
125 pid = fork ();
126 g_assert ( pid >= 0 );
127
128 if ( pid == 0 )
129 {
130 /* child listens */
131 /* This is a non SSL server */
132 thrift_socket_server (non_ssl_port);
133 exit (0);
134 } else {
135 /* parent connects, wait a bit for the socket to be created */
136 sleep (1);
137
138 /* open a connection and close it */
139 tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", non_ssl_port, &error);
140
141 transport = THRIFT_TRANSPORT (tSSLSocket);
142 g_assert (thrift_ssl_socket_open (transport, &error) == FALSE);
143 g_assert_cmpstr(error->message, == ,"Error while connect/bind: 68 -> Connection reset by peer");
144 g_clear_error (&error);
145 g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
146 thrift_ssl_socket_close (transport, NULL);
147 g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
148
149 /* test close failure */
150 THRIFT_SOCKET(tSSLSocket)->sd = -1;
151 thrift_ssl_socket_close (transport, NULL);
152 g_object_unref (tSSLSocket);
153
154 /* try a hostname lookup failure */
155 tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost.broken", non_ssl_port, &error);
156 transport = THRIFT_TRANSPORT (tSSLSocket);
157 g_assert (thrift_ssl_socket_open (transport, &error) == FALSE);
158 snprintf(errormsg, 255, "host lookup failed for localhost.broken:%d - Unknown host", non_ssl_port);
159 g_assert_cmpstr(error->message, ==, errormsg);
160 g_clear_error (&error);
161 g_object_unref (tSSLSocket);
162 error = NULL;
163
164 /* try an error call to socket() */
165 /*
166 tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", port, &error);
167 transport = THRIFT_TRANSPORT (tSSLSocket);
168 socket_error = 1;
169 assert (thrift_ssl_socket_open (transport, &error) == FALSE);
170 socket_error = 0;
171 g_object_unref (tSSLSocket);
172 g_error_free (error);
173 */
174 }
175 }
176
177 static void
178 test_ssl_write_invalid_socket(void)
179 {
180 ThriftSSLSocket *tSSLSocket = NULL;
181 ThriftTransport *transport = NULL;
182 GError *error=NULL;
183 char buffer[] = "this must not break";
184
185 /* open a connection and close it */
186 tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", 51188+1, &error);
187
188 transport = THRIFT_TRANSPORT (tSSLSocket);
189 g_assert (thrift_ssl_socket_open (transport, NULL) == FALSE);
190 g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
191
192 /* FIXME This must be tested but since the assertion inside thrift_ssl_socket_write breaks the test unit
193 it's disabled. They idea is to disable trap/coredump during this test
194 g_assert (thrift_ssl_socket_write(transport, buffer, sizeof(buffer), &error) == FALSE);
195 g_message ("write_failed_with_error: %s",
196 error != NULL ? error->message : "No");
197 g_clear_error (&error);
198 */
199 thrift_ssl_socket_close (transport, NULL);
200 g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
201
202 /* test close failure */
203 THRIFT_SOCKET(tSSLSocket)->sd = -1;
204 thrift_ssl_socket_close (transport, NULL);
205 g_object_unref (tSSLSocket);
206 }
207
208
209
210 /**
211 * Print the common name of certificate
212 */
213 unsigned char * get_cn_name(X509_NAME* const name)
214 {
215 int idx = -1;
216 unsigned char *utf8 = NULL;
217
218 do
219 {
220 if(!name) break; /* failed */
221
222 idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
223 if(!(idx > -1)) break; /* failed */
224
225 X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, idx);
226 if(!entry) break; /* failed */
227
228 ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
229 if(!data) break; /* failed */
230
231 int length = ASN1_STRING_to_UTF8(&utf8, data);
232 if(!utf8 || !(length > 0)) break; /* failed */
233
234 } while (0);
235 return utf8;
236 }
237
238 /*
239 * Handle IPV4 and IPV6 addr
240 */
241 void *get_in_addr(struct sockaddr *sa)
242 {
243 if (sa->sa_family == AF_INET)
244 return &(((struct sockaddr_in*)sa)->sin_addr);
245 return &(((struct sockaddr_in6*)sa)->sin6_addr);
246 }
247
248 int verify_ip(char * hostname, struct sockaddr_storage *addr)
249 {
250 struct addrinfo *addr_info,*p;
251 struct addrinfo hints;
252 int res;
253 int retval = 0;
254
255
256 memset(&hints, 0, sizeof (struct addrinfo));
257 hints.ai_family = AF_UNSPEC; /* use AF_INET6 to force IPv6 */
258 hints.ai_socktype = SOCK_STREAM;
259
260
261 if ( (res = getaddrinfo(hostname, NULL, &hints, &addr_info) ) != 0)
262 {
263 /* get the host info */
264 g_error("Cannot get the host address");
265 return retval;
266 }
267 /* loop through all the results and connect to the first we can */
268 char dnshost[INET6_ADDRSTRLEN]; /* bigger addr supported IPV6 */
269 char socket_ip[INET6_ADDRSTRLEN];
270 if(inet_ntop(addr->ss_family, get_in_addr(addr), socket_ip, INET6_ADDRSTRLEN)==socket_ip){
271 g_debug("We are connected to host %s checking against certificate...", socket_ip);
272 int sizeip = socket_ip!=NULL ? strlen(socket_ip) : 0;
273 for(p = addr_info; p != NULL; p = p->ai_next) {
274 if(inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), dnshost, INET6_ADDRSTRLEN)==dnshost){
275 if(dnshost!=NULL){
276 g_info("DNS address [%i -> %s]", p->ai_addr, dnshost);
277 if(!strncmp(dnshost, socket_ip, sizeip)){
278 retval=1;
279 break; /* if we get here, we must have connected successfully */
280 }
281 }
282 }
283 }
284 }
285
286 if(addr_info)
287 freeaddrinfo(addr_info);
288
289 return retval;
290 }
291
292 static void
293 read_from_file(char *buffer, long size, const char *file_name)
294 {
295 char ch;
296 long index=0;
297 FILE *fp;
298
299 fp = fopen(file_name,"r"); /* read mode */
300
301 if( fp == NULL )
302 {
303 perror("Error while opening the file.\n");
304 exit(EXIT_FAILURE);
305 }
306
307 printf("The contents of %s file are :\n", file_name);
308
309 while(index<size && ( ch = fgetc(fp) ) != EOF ){
310 buffer[index++] = ch;
311 }
312
313 fclose(fp);
314 }
315
316 #define ISSUER_CN_PINNING "The Apache Software Foundation"
317 #define SUBJECT_CN_PINNING "localhost"
318 #define CERT_SERIAL_NUMBER "1"
319
320 gboolean verify_certificate_sn(X509 *cert, const unsigned char *serial_number)
321 {
322 gboolean retval = FALSE;
323
324 ASN1_INTEGER *serial = X509_get_serialNumber(cert);
325
326 BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL);
327 if (!bn) {
328 fprintf(stderr, "unable to convert ASN1INTEGER to BN\n");
329 return EXIT_FAILURE;
330 }
331 char *tmp = BN_bn2dec(bn);
332 if (!tmp) {
333 g_warning(stderr, "unable to convert BN to decimal string.\n");
334 BN_free(bn);
335 return EXIT_FAILURE;
336 }
337 /*
338 if (strlen(tmp) >= len) {
339 g_warn(stderr, "buffer length shorter than serial number\n");
340 BN_free(bn);
341 OPENSSL_free(tmp);
342 return EXIT_FAILURE;
343 }
344 */
345 if(!strncmp(serial_number, tmp, strlen(serial_number))){
346 retval=TRUE;
347 }else{
348 g_warning("Serial number is not valid");
349 }
350
351 BN_free(bn);
352 OPENSSL_free(tmp);
353 return retval;
354 }
355
356 gboolean my_access_manager(ThriftTransport * transport, X509 *cert, struct sockaddr_storage *addr, GError **error)
357 {
358 ThriftSSLSocket *sslSocket = THRIFT_SSL_SOCKET (transport);
359
360 g_info("Processing access to the server");
361 X509_NAME* iname = cert ? X509_get_issuer_name(cert) : NULL;
362 X509_NAME* sname = cert ? X509_get_subject_name(cert) : NULL;
363
364 /* Issuer is the authority we trust that warrants nothing useful */
365 const unsigned char * issuer = get_cn_name(iname);
366 if(issuer){
367 gboolean valid = TRUE;
368 g_info("Issuer (cn) %s", issuer);
369
370 /* Issuer pinning */
371 if(strncmp(ISSUER_CN_PINNING, issuer, strlen(ISSUER_CN_PINNING))){
372 g_warning("The Issuer of the certificate is not valid");
373 valid=FALSE;
374 }
375 OPENSSL_free(issuer);
376 if(!valid)
377 return valid;
378 }
379
380
381 /* Subject is who the certificate is issued to by the authority */
382 const unsigned char * subject = get_cn_name(sname);
383 if(subject){
384 g_info("Subject (cn) %s", subject);
385 gboolean valid = TRUE;
386
387 /* Subject pinning */
388 if(strncmp(SUBJECT_CN_PINNING, subject, strlen(SUBJECT_CN_PINNING))){
389 g_warning("The subject of the certificate is not valid");
390 valid=FALSE;
391 }
392
393 if(!valid)
394 return valid;
395
396 /* Host pinning */
397 if(verify_ip(subject, addr)){
398 g_info("Verified subject");
399 }else{
400 g_info("Cannot verify subject");
401 valid=FALSE;
402 }
403 OPENSSL_free(subject);
404
405 if(!valid)
406 return valid;
407 }
408
409 if(!verify_certificate_sn(cert, CERT_SERIAL_NUMBER)){
410 return FALSE;
411 }else{
412 g_info("Verified serial number");
413 }
414
415 return TRUE;
416
417 }
418
419
420
421
422 #ifdef BUILD_SERVER
423 static void
424 test_ssl_authorization_manager(void)
425 {
426 int status=0;
427 pid_t pid;
428 ThriftSSLSocket *tSSLsocket = NULL;
429 ThriftTransport *transport = NULL;
430 /* int port = 51199; */
431 int port = 443;
432 GError *error=NULL;
433
434 guchar buf[17] = TEST_DATA; /* a buffer */
435
436 /*
437 pid = fork ();
438 g_assert ( pid >= 0 );
439
440 if ( pid == 0 )
441 {
442 thrift_ssl_socket_server (port);
443 exit (0);
444 } else {
445 */
446 /* parent connects, wait a bit for the socket to be created */
447 sleep (1);
448
449 /* Test against level2 owncloud certificate */
450 tSSLsocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", port, &error);
451 thrift_ssl_socket_set_manager(tSSLsocket, my_access_manager); /* Install pinning manager */
452 /* thrift_ssl_load_cert_from_file(tSSLsocket, "./owncloud.level2crm.pem"); */
453 unsigned char cert_buffer[65534];
454 read_from_file(cert_buffer, 65534, "../../keys/client.pem");
455 if(!thrift_ssl_load_cert_from_buffer(tSSLsocket, cert_buffer)){
456 g_warning("Certificates cannot be loaded!");
457 }
458
459 transport = THRIFT_TRANSPORT (tSSLsocket);
460 g_assert (thrift_ssl_socket_open (transport, NULL) == TRUE);
461 g_assert (thrift_ssl_socket_is_open (transport));
462
463 thrift_ssl_socket_write (transport, buf, 17, NULL);
464
465 /* write fail */
466 send_error = 1;
467 /*
468 thrift_ssl_socket_write (transport, buf, 1, NULL);
469 send_error = 0;
470 thrift_ssl_socket_write_end (transport, NULL);
471 thrift_ssl_socket_flush (transport, NULL);
472 */
473 thrift_ssl_socket_close (transport, NULL);
474 g_object_unref (tSSLsocket);
475
476 /* g_assert ( wait (&status) == pid ); */
477 g_assert ( status == 0 );
478 /* } */
479 }
480 #endif
481
482
483 static void
484 thrift_socket_server (const int port)
485 {
486 int bytes = 0;
487 ThriftServerTransport *transport = NULL;
488 ThriftTransport *client = NULL;
489 guchar buf[10]; /* a buffer */
490 guchar match[10] = TEST_DATA;
491
492 ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
493 "port", port, NULL);
494
495 transport = THRIFT_SERVER_TRANSPORT (tsocket);
496 thrift_server_transport_listen (transport, NULL);
497 client = thrift_server_transport_accept (transport, NULL);
498 g_assert (client != NULL);
499
500 /* read 10 bytes */
501 bytes = thrift_ssl_socket_read (client, buf, 10, NULL);
502 g_assert (bytes == 10); /* make sure we've read 10 bytes */
503 g_assert ( memcmp(buf, match, 10) == 0 ); /* make sure what we got matches */
504
505 /* failed read */
506 recv_error = 1;
507 thrift_ssl_socket_read (client, buf, 1, NULL);
508 recv_error = 0;
509
510 thrift_ssl_socket_read_end (client, NULL);
511 thrift_ssl_socket_close (client, NULL);
512 g_object_unref (tsocket);
513 g_object_unref (client);
514 }
515
516 int
517 main(int argc, char *argv[])
518 {
519 int retval;
520 #if (!GLIB_CHECK_VERSION (2, 36, 0))
521 g_type_init();
522 #endif
523
524 g_test_init (&argc, &argv, NULL);
525
526 thrift_ssl_socket_initialize_openssl();
527
528 g_test_add_func ("/testtransportsslsocket/CreateAndDestroy", test_ssl_create_and_destroy);
529 g_test_add_func ("/testtransportsslsocket/CreateAndSetProperties", test_ssl_create_and_set_properties);
530 g_test_add_func ("/testtransportsslsocket/OpenAndCloseNonSSLServer", test_ssl_open_and_close_non_ssl_server);
531 g_test_add_func ("/testtransportsslsocket/OpenAndWriteInvalidSocket", test_ssl_write_invalid_socket);
532
533
534
535
536 retval = g_test_run ();
537
538 thrift_ssl_socket_finalize_openssl();
539
540 return retval;
541 }
542