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
my_socket(int domain,int type,int protocol)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
my_recv(int socket,void * buffer,size_t length,int flags)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
my_send(int socket,const void * buffer,size_t length,int flags)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
test_ssl_create_and_destroy(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
test_ssl_create_and_set_properties(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 = (GObject *)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
test_ssl_open_and_close_non_ssl_server(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
test_ssl_write_invalid_socket(void)178 test_ssl_write_invalid_socket(void)
179 {
180 ThriftSSLSocket *tSSLSocket = NULL;
181 ThriftTransport *transport = NULL;
182 GError *error=NULL;
183
184 /* open a connection and close it */
185 tSSLSocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", 51188+1, &error);
186
187 transport = THRIFT_TRANSPORT (tSSLSocket);
188 g_assert (thrift_ssl_socket_open (transport, NULL) == FALSE);
189 g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
190
191 /* FIXME This must be tested but since the assertion inside thrift_ssl_socket_write breaks the test unit
192 it's disabled. They idea is to disable trap/coredump during this test
193 g_assert (thrift_ssl_socket_write(transport, buffer, sizeof(buffer), &error) == FALSE);
194 g_message ("write_failed_with_error: %s",
195 error != NULL ? error->message : "No");
196 g_clear_error (&error);
197 */
198 thrift_ssl_socket_close (transport, NULL);
199 g_assert (thrift_ssl_socket_is_open (transport) == FALSE);
200
201 /* test close failure */
202 THRIFT_SOCKET(tSSLSocket)->sd = -1;
203 thrift_ssl_socket_close (transport, NULL);
204 g_object_unref (tSSLSocket);
205 }
206
207
208
209 /**
210 * Print the common name of certificate
211 */
get_cn_name(X509_NAME * const name)212 unsigned char * get_cn_name(X509_NAME* const name)
213 {
214 int idx = -1;
215 unsigned char *utf8 = NULL;
216
217 do
218 {
219 if(!name) break; /* failed */
220
221 idx = X509_NAME_get_index_by_NID(name, NID_commonName, -1);
222 if(!(idx > -1)) break; /* failed */
223
224 X509_NAME_ENTRY* entry = X509_NAME_get_entry(name, idx);
225 if(!entry) break; /* failed */
226
227 ASN1_STRING* data = X509_NAME_ENTRY_get_data(entry);
228 if(!data) break; /* failed */
229
230 int length = ASN1_STRING_to_UTF8(&utf8, data);
231 if(!utf8 || !(length > 0)) break; /* failed */
232
233 } while (0);
234 return utf8;
235 }
236
237 /*
238 * Handle IPV4 and IPV6 addr
239 */
get_in_addr(struct sockaddr * sa)240 void *get_in_addr(struct sockaddr *sa)
241 {
242 if (sa->sa_family == AF_INET)
243 return &(((struct sockaddr_in*)sa)->sin_addr);
244 return &(((struct sockaddr_in6*)sa)->sin6_addr);
245 }
246
verify_ip(char * hostname,struct sockaddr_storage * addr)247 int verify_ip(char * hostname, struct sockaddr_storage *addr)
248 {
249 struct addrinfo *addr_info,*p;
250 struct addrinfo hints;
251 int res;
252 int retval = 0;
253
254
255 memset(&hints, 0, sizeof (struct addrinfo));
256 hints.ai_family = AF_UNSPEC; /* use AF_INET6 to force IPv6 */
257 hints.ai_socktype = SOCK_STREAM;
258
259
260 if ( (res = getaddrinfo(hostname, NULL, &hints, &addr_info) ) != 0)
261 {
262 /* get the host info */
263 g_error("Cannot get the host address");
264 return retval;
265 }
266 /* loop through all the results and connect to the first we can */
267 char dnshost[INET6_ADDRSTRLEN]; /* bigger addr supported IPV6 */
268 char socket_ip[INET6_ADDRSTRLEN];
269 if(inet_ntop(addr->ss_family, get_in_addr((struct sockaddr*)addr), socket_ip, INET6_ADDRSTRLEN)==socket_ip){
270 g_debug("We are connected to host %s checking against certificate...", socket_ip);
271 int sizeip = socket_ip!=NULL ? strlen(socket_ip) : 0;
272 for(p = addr_info; p != NULL; p = p->ai_next) {
273 if(inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), dnshost, INET6_ADDRSTRLEN)==dnshost){
274 if(dnshost!=NULL){
275 g_info("DNS address [%i -> %s]", ((guint32)(p->ai_addrlen)), dnshost);
276 if(!strncmp(dnshost, socket_ip, sizeip)){
277 retval=1;
278 break; /* if we get here, we must have connected successfully */
279 }
280 }
281 }
282 }
283 }
284
285 if(addr_info)
286 freeaddrinfo(addr_info);
287
288 return retval;
289 }
290
291 static void
read_from_file(char * buffer,long size,const char * file_name)292 read_from_file(char *buffer, long size, const char *file_name)
293 {
294 char ch;
295 long index=0;
296 FILE *fp;
297
298 fp = fopen(file_name,"r"); /* read mode */
299
300 if( fp == NULL )
301 {
302 perror("Error while opening the file.\n");
303 exit(EXIT_FAILURE);
304 }
305
306 printf("The contents of %s file are :\n", file_name);
307
308 while(index<size && ( ch = fgetc(fp) ) != EOF ){
309 buffer[index++] = ch;
310 }
311
312 fclose(fp);
313 }
314
315 #define ISSUER_CN_PINNING "The Apache Software Foundation"
316 #define SUBJECT_CN_PINNING "localhost"
317 #define CERT_SERIAL_NUMBER "1"
318
verify_certificate_sn(X509 * cert,const unsigned char * serial_number)319 gboolean verify_certificate_sn(X509 *cert, const unsigned char *serial_number)
320 {
321 gboolean retval = FALSE;
322
323 ASN1_INTEGER *serial = X509_get_serialNumber(cert);
324
325 BIGNUM *bn = ASN1_INTEGER_to_BN(serial, NULL);
326 if (!bn) {
327 fprintf(stderr, "unable to convert ASN1INTEGER to BN\n");
328 return EXIT_FAILURE;
329 }
330 char *tmp = BN_bn2dec(bn);
331 if (!tmp) {
332 g_warning((const char*)stderr, "unable to convert BN to decimal string.\n");
333 BN_free(bn);
334 return EXIT_FAILURE;
335 }
336 /*
337 if (strlen(tmp) >= len) {
338 g_warn(stderr, "buffer length shorter than serial number\n");
339 BN_free(bn);
340 OPENSSL_free(tmp);
341 return EXIT_FAILURE;
342 }
343 */
344 if(!strncmp((const char*)serial_number, tmp, strlen((const char*)serial_number))){
345 retval=TRUE;
346 }else{
347 g_warning("Serial number is not valid");
348 }
349
350 BN_free(bn);
351 OPENSSL_free(tmp);
352 return retval;
353 }
354
my_access_manager(ThriftTransport * transport,X509 * cert,struct sockaddr_storage * addr,GError ** error)355 gboolean my_access_manager(ThriftTransport * transport, X509 *cert, struct sockaddr_storage *addr, GError **error)
356 {
357 ThriftSSLSocket *sslSocket = THRIFT_SSL_SOCKET (transport);
358 THRIFT_UNUSED_VAR (error);
359 THRIFT_UNUSED_VAR (sslSocket);
360
361 g_info("Processing access to the server");
362 X509_NAME* iname = cert ? X509_get_issuer_name(cert) : NULL;
363 X509_NAME* sname = cert ? X509_get_subject_name(cert) : NULL;
364
365 /* Issuer is the authority we trust that warrants nothing useful */
366 const unsigned char * issuer = get_cn_name(iname);
367 if(issuer){
368 gboolean valid = TRUE;
369 g_info("Issuer (cn) %s", issuer);
370
371 /* Issuer pinning */
372 if(strncmp(ISSUER_CN_PINNING, (const char*)issuer, strlen(ISSUER_CN_PINNING))){
373 g_warning("The Issuer of the certificate is not valid");
374 valid=FALSE;
375 }
376 OPENSSL_free((void*)issuer);
377 if(!valid)
378 return valid;
379 }
380
381
382 /* Subject is who the certificate is issued to by the authority */
383 const unsigned char * subject = get_cn_name(sname);
384 if(subject){
385 g_info("Subject (cn) %s", subject);
386 gboolean valid = TRUE;
387
388 /* Subject pinning */
389 if(strncmp(SUBJECT_CN_PINNING, (const char*)subject, strlen(SUBJECT_CN_PINNING))){
390 g_warning("The subject of the certificate is not valid");
391 valid=FALSE;
392 }
393
394 if(!valid)
395 return valid;
396
397 /* Host pinning */
398 if(verify_ip((char*)subject, addr)){
399 g_info("Verified subject");
400 }else{
401 g_info("Cannot verify subject");
402 valid=FALSE;
403 }
404 OPENSSL_free((void*)subject);
405
406 if(!valid)
407 return valid;
408 }
409
410 if(!verify_certificate_sn(cert, (const unsigned char*)CERT_SERIAL_NUMBER)){
411 return FALSE;
412 }else{
413 g_info("Verified serial number");
414 }
415
416 return TRUE;
417
418 }
419
420
421
422
423 #ifdef BUILD_SERVER
424 static void
test_ssl_authorization_manager(void)425 test_ssl_authorization_manager(void)
426 {
427 int status=0;
428 pid_t pid;
429 ThriftSSLSocket *tSSLsocket = NULL;
430 ThriftTransport *transport = NULL;
431 /* int port = 51199; */
432 int port = 443;
433 GError *error=NULL;
434
435 guchar buf[17] = TEST_DATA; /* a buffer */
436
437 /*
438 pid = fork ();
439 g_assert ( pid >= 0 );
440
441 if ( pid == 0 )
442 {
443 thrift_ssl_socket_server (port);
444 exit (0);
445 } else {
446 */
447 /* parent connects, wait a bit for the socket to be created */
448 sleep (1);
449
450 /* Test against level2 owncloud certificate */
451 tSSLsocket = thrift_ssl_socket_new_with_host(SSLTLS, "localhost", port, &error);
452 thrift_ssl_socket_set_manager(tSSLsocket, my_access_manager); /* Install pinning manager */
453 /* thrift_ssl_load_cert_from_file(tSSLsocket, "./owncloud.level2crm.pem"); */
454 unsigned char cert_buffer[65534];
455 read_from_file(cert_buffer, 65534, "../../keys/client.pem");
456 if(!thrift_ssl_load_cert_from_buffer(tSSLsocket, cert_buffer)){
457 g_warning("Certificates cannot be loaded!");
458 }
459
460 transport = THRIFT_TRANSPORT (tSSLsocket);
461 g_assert (thrift_ssl_socket_open (transport, NULL) == TRUE);
462 g_assert (thrift_ssl_socket_is_open (transport));
463
464 thrift_ssl_socket_write (transport, buf, 17, NULL);
465
466 /* write fail */
467 send_error = 1;
468 /*
469 thrift_ssl_socket_write (transport, buf, 1, NULL);
470 send_error = 0;
471 thrift_ssl_socket_write_end (transport, NULL);
472 thrift_ssl_socket_flush (transport, NULL);
473 */
474 thrift_ssl_socket_close (transport, NULL);
475 g_object_unref (tSSLsocket);
476
477 /* g_assert ( wait (&status) == pid ); */
478 g_assert ( status == 0 );
479 /* } */
480 }
481 #endif
482
483
484 static void
thrift_socket_server(const int port)485 thrift_socket_server (const int port)
486 {
487 int bytes = 0;
488 ThriftServerTransport *transport = NULL;
489 ThriftTransport *client = NULL;
490 guchar buf[10]; /* a buffer */
491 guchar match[] = TEST_DATA;
492
493 ThriftServerSocket *tsocket = g_object_new (THRIFT_TYPE_SERVER_SOCKET,
494 "port", port, NULL);
495
496 transport = THRIFT_SERVER_TRANSPORT (tsocket);
497 thrift_server_transport_listen (transport, NULL);
498 client = thrift_server_transport_accept (transport, NULL);
499 g_assert (client != NULL);
500
501 /* read 10 bytes */
502 bytes = thrift_ssl_socket_read (client, buf, 10, NULL);
503 g_assert (bytes == 10); /* make sure we've read 10 bytes */
504 g_assert ( memcmp(buf, match, 10) == 0 ); /* make sure what we got matches */
505
506 /* failed read */
507 recv_error = 1;
508 thrift_ssl_socket_read (client, buf, 1, NULL);
509 recv_error = 0;
510
511 thrift_ssl_socket_read_end (client, NULL);
512 thrift_ssl_socket_close (client, NULL);
513 g_object_unref (tsocket);
514 g_object_unref (client);
515 }
516
517 int
main(int argc,char * argv[])518 main(int argc, char *argv[])
519 {
520 int retval;
521 #if (!GLIB_CHECK_VERSION (2, 36, 0))
522 g_type_init();
523 #endif
524
525 g_test_init (&argc, &argv, NULL);
526
527 thrift_ssl_socket_initialize_openssl();
528
529 g_test_add_func ("/testtransportsslsocket/CreateAndDestroy", test_ssl_create_and_destroy);
530 g_test_add_func ("/testtransportsslsocket/CreateAndSetProperties", test_ssl_create_and_set_properties);
531 g_test_add_func ("/testtransportsslsocket/OpenAndCloseNonSSLServer", test_ssl_open_and_close_non_ssl_server);
532 g_test_add_func ("/testtransportsslsocket/OpenAndWriteInvalidSocket", test_ssl_write_invalid_socket);
533
534
535
536
537 retval = g_test_run ();
538
539 thrift_ssl_socket_finalize_openssl();
540
541 return retval;
542 }
543
544