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