Communication Samples

Some C code examples

myssl.c
Go to the documentation of this file.
1 /**
2 *************************************************************
3 * @file myssl.c
4 * @brief SSL Client HTTP Connection example getting information
5 * and verifying certificates.
6 * These are just some SSL notes
7 *
8 * @author Gaspar Fernández <blakeyed@totaki.com>
9 * @version
10 * @date 17 abr 2015
11 *
12 * To compile:
13 * $ gcc -o myssl myssl.c -lcrypto -lssl
14 *
15 *************************************************************/
16 
17 #include <time.h>
18 #include <unistd.h>
19 #include <stdio.h>
20 #include <string.h>
21 #include <sys/socket.h>
22 #include <sys/types.h>
23 #include <netinet/in.h>
24 #include <netdb.h>
25 #include <stdlib.h>
26 #include <errno.h>
27 #include <openssl/rand.h>
28 #include <openssl/ssl.h>
29 #include <openssl/err.h>
30 #include <sys/select.h>
31 #include <sys/time.h>
32 
33 /** Where to look for the Certificate Authorities */
34 #define CAPATH "/etc/ssl/certs"
35 /** 16Kb SSL_read block size */
36 #define BUFFERSIZE 16384
37 /** For temporary strings */
38 #define STRBUFFERSIZE 256
39 /** User agent string */
40 #define USERAGENT "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:37.0) Gecko/20100101 Firefox/37.0"
41 /** CRLF */
42 #define CRLF "\r\n"
43 
44 /* Useful Structure. Just to have everything in one place and avoid
45  passing lot of arguments to the functions. */
46 
47 /** sslc handles SSL and SSL_CTX and socket */
48 typedef struct
49 {
50  /** socket handler */
51  int skt;
52  /** error, if any */
53  int err;
54  /** SSL handler */
55  SSL* ssl;
56  /** SSL Context */
57  SSL_CTX* ctx;
58 } Sslc;
59 
60 /* First some useful stuff */
61 
62 /**
63  * Transforms ASN1 time sring to time_t (except milliseconds and time zone)
64  * Ideas from: http://stackoverflow.com/questions/10975542/asn1-time-conversion
65  *
66  * @param time SSL ASN1_TIME pointer
67  * @param tmt time_t pointer to write to
68  *
69  * @return int 0 if OK, <0 if anything goes wrong
70  */
71 int ASN1_TIME_to_time_t(ASN1_TIME* time, time_t *tmt);
72 
73 /**
74  * Extract a substring from origin into buffer, updating starting
75  * value to call in chain. Used by ASN1_TIME_to_time_t to extract
76  * substrings easyly
77  *
78  * @param buffer Where to write to
79  * @param origin Original string
80  * @param from Where to start from.
81  * Updated to the last position after end.
82  * @param size Characters to extract.
83  *
84  * @return char* reference to buffer
85  */
86 char* join(char* buffer, const char* origin, size_t *from, size_t size);
87 
88 /**
89  * Check certificate validity
90  *
91  * @param certificate Certificate to check
92  *
93  * @return error code (0 is OK. See x509_vfy.h for
94  * constants (X509_V_ERR_UNABLE_*). You can also
95  * use X509_verify_cert_error_string(long int) to
96  * see the error string
97  */
98 long int check_certificate_validity(X509* certificate);
99 
100 /**
101  * Creates a basic TCP client connection to a server on a port.
102  * Uses simple sockets
103  *
104  * @param h Our structure. Only the socket will be used
105  * @param server Where to connect
106  * @param port The port to use (443 for HTTPS)
107  *
108  * @return int (0 if OK, else fail)
109  */
110 int TCP_Connection(Sslc* h, char* server, int port);
111 
112 /**
113  * Uses select to test if there is anything waiting to be read.
114  *
115  * @param h Our structure. Only the socket will be used
116  * @param timeout Timeout before giving up
117  *
118  * @return (0 timeout, 1 data waiting, <0 fail)
119  */
120 int TCP_select(Sslc* h, double timeout);
121 
122 /**
123  * SSL initialization and handshake
124  *
125  * @param h Our structure.
126  *
127  * @return (0 if OK, else fail)
128  */
129 int SSL_Connection(Sslc* h);
130 
131 /**
132  * SSL send. To be called instead of send. It will send data through
133  * the socket and decode information, or even perform a handshake
134  * if needed.
135  *
136  * @param h Our structure.
137  * @param msg Message to send
138 
139  * @return (0 if OK, else fail)
140  */
141 int SSL_send(Sslc* h, char* msg);
142 
143 /**
144  * SSL recv. To be called instead of recs. It will read the socket
145  * and decode information, or even perform a handshake if needed
146  *
147  * @param h Our structure.
148  * @param data Data to be read (caution a pointer by
149  * reference that must be freed manually
150 
151  * @return (0 if OK, else fail)
152  */
153 int SSL_recv(Sslc* h, char** data);
154 
155 /**
156  * Prints out SSL information: SSL Version, cipher used and certificate
157  * information.
158  *
159  * @param h Our structure.
160  *
161  * @return void
162  */
163 void SSL_print_info(Sslc* h);
164 
165 /**
166  * Prints out certificate information. Run throught the entries, print the
167  * not before and not after information and verify the certificate.
168  *
169  * @param cert The certificate to check
170  *
171  * @return void
172  */
173 void SSL_print_certificate_info(X509* cert);
174 
175 /**
176  * Gets cipher description in a string
177  * Please free the resulting string, don't do it like me ;)
178  *
179  * @param cipher Cipher
180  *
181  * @return String with the description
182  */
183 char *SSL_cipher_description(SSL_CIPHER* cipher);
184 
185 /**
186  * Gets a string with the time_t into a string
187  *
188  * @param buffer Buffer to write to
189  * @param bufsize Total buffer size
190  * @param format Date/Time format (@see strftime())
191  * @param tim Time
192  *
193  * @return buffer
194  */
195 char *time_t_to_str(char *buffer, size_t bufsize, const char* format, time_t *tim);
196 
197 /**
198  * Prints ASN1_TIME on screen
199  *
200  * @param asn1time Time to write
201  * @param pre_string String to write before the date
202  * @param dateformat Date format (@see strftime())
203  *
204  * @return void
205  */
206 void print_time(ASN1_TIME* asn1time, char* pre_string, char* dateformat);
207 
208 
209 /**
210  * Prints program usage
211  *
212  * @param executable Program executable (argv[0])
213  *
214  * @return void
215  */
216 void print_usage(char* executable);
217 
218 /**
219  * Prints a tragic error and exit
220  *
221  * @param msg Error text
222  *
223  * @return void
224  */
225 void panic(char *msg);
226 
227 int main(int argc, char *argv[])
228 {
229  Sslc sslc;
230  char *response;
231  char *server = argv[1];
232  int port;
233  char *httpquerytemplate ="GET / HTTP/1.1" CRLF "Host: %s" CRLF "User-Agent: %s" CRLF CRLF;
234  char httpquery[1024];
235 
236  /* What will be sent to the server */
237  sprintf (httpquery, httpquerytemplate, server, USERAGENT);
238 
239  if (argc<2)
240  print_usage(argv[0]);
241 
242  if (argc>2)
243  port = atoi (argv[2]);
244  else
245  port = 443; /* default https port */
246 
247  if (TCP_Connection(&sslc, server, port)<0)
248  panic ("Couldn't connect host");
249 
250  if (SSL_Connection(&sslc)<0)
251  panic ("Couldn't stablish secure connection");
252 
253  if (SSL_send(&sslc, httpquery)<0)
254  panic ("Couldn't send anything to the server");
255 
256  SSL_print_info(&sslc);
257 
258  if (SSL_recv(&sslc, &response)<0)
259  panic ("Couldn't receive the message");
260 
261  printf ("Received %lu bytes\n", strlen(response));
262 
263  free(response);
264 
265  return EXIT_SUCCESS;
266 }
267 
268 char* join(char* buffer, const char* origin, size_t *from, size_t size)
269 {
270  size_t i=0;
271  while (i<size)
272  {
273  buffer[i++] = origin[(*from)++];
274  }
275  buffer[i] = '\0';
276  return buffer;
277 }
278 
279 int ASN1_TIME_to_time_t(ASN1_TIME* time, time_t *tmt)
280 {
281  const char* data = (char*)time->data;
282  size_t p = 0;
283  char buf[5];
284  struct tm t;
285 
286  memset(&t, 0, sizeof(t));
287  size_t datalen = strlen(data);
288 
289  if (time->type == V_ASN1_UTCTIME) {/* two digit year */
290  /* error checking YYMMDDHH at least */
291  if (datalen<8)
292  return -1;
293  t.tm_year = atoi (join(buf, data, &p, 2));
294  if (t.tm_year<70)
295  t.tm_year += 100;
296  datalen = strlen(data+2);
297  } else if (time->type == V_ASN1_GENERALIZEDTIME) {/* four digit year */
298  /* error checking YYYYMMDDHH at least*/
299  if (datalen<10)
300  return -1;
301 
302  t.tm_year = atoi (join(buf, data, &p, 4));
303  t.tm_year -= 1900;
304  datalen = strlen(data+4);
305  }
306 
307  /* the year is out of datalen. Now datalen is fixed */
308 
309  t.tm_mon = atoi (join(buf, data, &p, 2))-1; /* January is 0 for time_t */
310  t.tm_mday= atoi (join(buf, data, &p, 2));
311  t.tm_hour= atoi (join(buf, data, &p, 2));
312 
313  if (datalen<8)
314  return !(*tmt = mktime(&t));
315  t.tm_min = atoi (join(buf, data, &p, 2));
316 
317  if (datalen<10)
318  return !(*tmt = mktime(&t));
319  t.tm_sec = atoi (join(buf, data, &p, 2));
320 
321  /* Ignore millisecnds and time zone */
322  return !(*tmt = mktime(&t));
323 }
324 
325 long int check_certificate_validity(X509* certificate)
326 {
327  int status;
328  X509_STORE_CTX *ctx;
329  ctx = X509_STORE_CTX_new();
330  X509_STORE *store = X509_STORE_new();
331  X509_STORE_load_locations(store, NULL, CAPATH);
332  X509_STORE_add_cert(store, certificate);
333 
334  X509_STORE_CTX_init(ctx, store, certificate, NULL);
335 
336  status = X509_verify_cert(ctx);
337 
338  return ctx->error;
339 }
340 
341 int TCP_Connection(Sslc* h, char* server, int port)
342 {
343  h->err = 0;
344  struct hostent *host = gethostbyname (server);
345  struct sockaddr_in addr;
346 
347  if (host == NULL)
348  return -1; /* Couldn't get host */
349 
350  h->skt = socket (AF_INET, SOCK_STREAM, 0);
351  if (h->skt < 0)
352  {
353  return -2;
354  }
355  else
356  {
357  /* fill in address data */
358  addr.sin_family = AF_INET;
359  addr.sin_port = htons (port);
360  addr.sin_addr = *((struct in_addr *) host->h_addr);
361  bzero (&(addr.sin_zero), 8);
362 
363  /* connect */
364  h->err = connect (h->skt, (struct sockaddr *) &addr, sizeof (struct sockaddr));
365  if (h->err == -1)
366  {
367  return -3;
368  }
369  }
370  return 0;
371 }
372 
374 {
375  SSL_library_init(); /* not reentrant! */
376  SSL_load_error_strings();
377 
378  /* try SSL methods (TLSv1.2 ... SSLv3 and SSLv2 (caution! SSLv2 and SSLv3 are deprecated!) */
379  /* you could use TLSv1_client_method(), TLSv1_2_client_method()... */
380  h->ctx = SSL_CTX_new(SSLv23_client_method());
381  if (h->ctx == NULL)
382  return -3; /* Context not created */
383 
384  /* SSL will fail if cerificate can't be validated */
385  /* h->ctx->verify_mode = 1; */
386 
387  h->ssl = SSL_new (h->ctx);
388  if (h->ssl == NULL)
389  return -4; /* SSL struct not created */
390 
391  if (SSL_set_fd(h->ssl, h->skt) == 0)
392  return -5; /* Couldn't bind SSL with our connection */
393 
394  if (SSL_CTX_load_verify_locations(h->ctx, NULL, CAPATH) == 0)
395  return -6; /* Couldn't load verify locations */
396 
397  /* Verify depth. How many certs from the chain to verify */
398  /* -1 to verify all certificates. */
399  printf ("VDepth: %d\n", SSL_get_verify_depth(h->ssl));
400 
401  if (SSL_connect (h->ssl) < 1)
402  return -7; /* Couldn't finish SSL handshake */
403 
404  /* Get certificate information */
405  return 0;
406 }
407 
408 int SSL_send(Sslc* h, char* msg)
409 {
410  int bytes = SSL_write(h->ssl, msg, strlen(msg));
411  if (bytes<1)
412  h->err = bytes;
413 
414  return bytes;
415 }
416 
417 int TCP_select(Sslc* h, double timeout)
418 {
419  fd_set fds;
420  FD_ZERO(&fds);
421  FD_SET(h->skt, &fds);
422  fd_set *rset=&fds;
423  fd_set *wset=NULL;
424 
425  struct timeval tv;
426  tv.tv_sec = (int)(timeout);
427  tv.tv_usec = (int)((timeout - (int)(timeout)) * 1000000.0);
428 
429  int ret = select(h->skt+1, rset, wset, NULL, &tv);
430  return ret;
431 }
432 
433 int SSL_recv(Sslc* h, char** data)
434 {
435  size_t bytes, totalbytes = 0;
436  char buffer[BUFFERSIZE];
437  int sel;
438  size_t allocated = BUFFERSIZE;
439  *data = malloc (sizeof(char) * BUFFERSIZE);
440  *data[0] = '\0';
441 
442  while (1)
443  {
444  sel = TCP_select(h, 1);
445  if (sel<0)
446  return -6; /* select fail */
447  else if (sel==0)
448  {
449  return totalbytes;
450  }
451  /* We must include terminator after reading */
452  bytes = SSL_read (h->ssl, buffer, BUFFERSIZE -1);
453  buffer[bytes] = '\0';
454 
455  if (bytes<1)
456  return -7;
457 
458  if (totalbytes + bytes > allocated)
459  {
460  allocated+=BUFFERSIZE;
461  *data = realloc(*data, allocated * sizeof(char));
462  }
463  strcat(*data, buffer);
464  totalbytes+=bytes;
465  }
466  return totalbytes;
467 }
468 
470 {
471  long vresult = SSL_get_verify_result (h->ssl);
472  X509* cert;
473  STACK_OF(X509) *chain;
474  int i;
475 
476  printf ("Verify result: %ld (%s)\n", vresult, X509_verify_cert_error_string(vresult));
477  printf ("Verify mode: %d\n", SSL_get_verify_mode(h->ssl)); /* If it's 1 SSL will fail if not verified */
478  printf ("SSL version: %s\n", SSL_get_version(h->ssl));
479  printf ("Cipher name : %s\n", SSL_get_cipher_name(h->ssl));
480  printf ("Cipher version: %s\n", SSL_get_cipher_version(h->ssl));
481  printf ("Cipher description: %s\n", SSL_cipher_description((SSL_CIPHER*)SSL_get_current_cipher(h->ssl)));
482  printf ("--------------------------------------------\n");
483  printf ("Certificate:\n");
484  cert = SSL_get_peer_certificate(h->ssl);
485  if (cert)
486  {
488  /* X509_free(cert); */
489  }
490  else
491  fprintf(stderr, "------ ERROR: Peer certificate not present!!\n");
492 
493  printf ("--------------------------------------------\n");
494  printf ("The entire certificate chain: \n");
495  chain = SSL_get_peer_cert_chain(h->ssl);
496  if (chain)
497  {
498  printf ("Certificates on chain: %d\n", sk_X509_num(chain));
499  for (i=0; i<sk_X509_num(chain); i++)
500  {
501  cert = sk_X509_value(chain, i);
502  if (cert)
503  {
505  /* Not a good idea to free, it's an internal pointer and may
506  break something*/
507  /* X509_free(cert); */
508  printf (" ·················\n");
509  }
510  }
511  }
512  else
513  fprintf (stderr, "------- ERROR: Couldn't get certificate chain!!\n");
514 }
515 
517 {
518  X509_NAME *certname;
519  X509_NAME_ENTRY* entry;
520  char *s;
521  char buffer[STRBUFFERSIZE];
522  int i;
523 
524  certname = X509_get_subject_name(cert);
525  for (i=0; i< X509_NAME_entry_count(certname); i++)
526  {
527  entry = X509_NAME_get_entry(certname, i);
528  if (entry == NULL) /* error test. May exit the loop */
529  continue;
530 
531  /* extracted from X509_NAME_print_ex() */
532  int n = OBJ_obj2nid(entry->object);
533  if ((n == NID_undef) || ((s = (char*)OBJ_nid2sn(n)) == NULL))
534  {
535  i2t_ASN1_OBJECT(buffer, sizeof(buffer), entry->object);
536  s = buffer;
537  }
538  printf ("%s = %s\n", s, entry->value->data);
539  /* We must NOT free entries (they are internal pointers) */
540  }
541  print_time(X509_get_notBefore(cert), "Not Before", "%d/%m/%Y %H:%M:%S");
542  print_time(X509_get_notAfter(cert), "Not After", "%d/%m/%Y %H:%M:%S");
543  printf ("Valid certificate?: %s\n", X509_verify_cert_error_string(check_certificate_validity(cert)));
544 
545  /* We must NOT free X509_get_subject_name() objects */
546  /* X509_NAME_free(certname); */
547 }
548 
549 char *time_t_to_str(char *buffer, size_t bufsize, const char* format, time_t *tim)
550 {
551  struct tm _tm;
552  gmtime_r(tim, &_tm);
553  strftime(buffer, bufsize, format, &_tm);
554  return buffer;
555 }
556 
557 void print_time(ASN1_TIME* asn1time, char* pre_string, char* dateformat)
558 {
559  time_t tim;
560  char buffer[STRBUFFERSIZE];
561 
562  if (ASN1_TIME_to_time_t(asn1time, &tim)==0)
563  printf ("%s: %s\n", pre_string, time_t_to_str(buffer, STRBUFFERSIZE, dateformat, &tim));
564  else
565  printf ("%s: (error)\n", pre_string);
566 }
567 
568 char *SSL_cipher_description(SSL_CIPHER* cipher)
569 {
570  char *tmp = malloc(sizeof(char)*STRBUFFERSIZE);
571  tmp[0] = '\0';
572  if (cipher)
573  SSL_CIPHER_description(cipher, tmp, STRBUFFERSIZE);
574 
575  return tmp;
576 }
577 
578 void print_usage(char *executable)
579 {
580  fprintf(stderr, "You must specify a web server to connect through HTTPS and additionally a port:\n");
581  fprintf(stderr, " %s server [port]\n", executable);
582  fprintf(stderr, "------------\n\n");
583  exit(-1);
584 }
585 
586 void panic(char *msg)
587 {
588  fprintf (stderr, "Error: %s (errno %d, %s)\n", msg, errno, strerror(errno));
589  /* Print SSL errors */
590  ERR_print_errors_fp(stderr);
591  exit(2);
592 }
int err
Definition: myssl.c:53
int SSL_send(Sslc *h, char *msg)
Definition: myssl.c:408
#define CAPATH
Definition: myssl.c:34
int ASN1_TIME_to_time_t(ASN1_TIME *time, time_t *tmt)
Definition: myssl.c:279
char * join(char *buffer, const char *origin, size_t *from, size_t size)
Definition: myssl.c:268
void panic(char *msg)
Definition: myssl.c:586
void SSL_print_info(Sslc *h)
Definition: myssl.c:469
int skt
Definition: myssl.c:51
#define CRLF
Definition: myssl.c:42
void print_usage(char *executable)
Definition: myssl.c:578
int SSL_Connection(Sslc *h)
Definition: myssl.c:373
int TCP_select(Sslc *h, double timeout)
Definition: myssl.c:417
#define STRBUFFERSIZE
Definition: myssl.c:38
int TCP_Connection(Sslc *h, char *server, int port)
Definition: myssl.c:341
Definition: myssl.c:48
SSL_CTX * ctx
Definition: myssl.c:57
#define BUFFERSIZE
Definition: myssl.c:36
void print_time(ASN1_TIME *asn1time, char *pre_string, char *dateformat)
Definition: myssl.c:557
#define USERAGENT
Definition: myssl.c:40
long int check_certificate_validity(X509 *certificate)
Definition: myssl.c:325
char * SSL_cipher_description(SSL_CIPHER *cipher)
Definition: myssl.c:568
int SSL_recv(Sslc *h, char **data)
Definition: myssl.c:433
void SSL_print_certificate_info(X509 *cert)
Definition: myssl.c:516
SSL * ssl
Definition: myssl.c:55
char * time_t_to_str(char *buffer, size_t bufsize, const char *format, time_t *tim)
Definition: myssl.c:549