SSL/TLS connection

OpenBSD‘s netcat supports SSL/TLS connection, and uses libtls shipped by libressl under the hood. The -c option is used to denote using SSL/TLS:

  1. ......
  2. int usetls;/* use TLS */
  3. ......
  4. case'c':
  5. usetls =1;
  6. break;

There is a simple example which demonstrates how to leverage netcat as an https client:

  1. # nc -c www.google.com https
  2. GET / HTTP/1.1
  3. Host: www.google.com
  4. Connection: close
  5. HTTP/1.1200 OK
  6. Date:Fri,21Sep201808:19:19 GMT
  7. Expires:-1
  8. Cache-Control:private, max-age=0
  9. Content-Type: text/html; charset=ISO-8859-1
  10. P3P: CP="This is not a P3P policy! See g.co/p3phelp for more info."
  11. Server: gws
  12. X-XSS-Protection:1; mode=block
  13. X-Frame-Options: SAMEORIGIN
  14. ......

The following is a sketch of how to use libtls to implement server and client:

(1) Initialization:
No matter netcat works as serve or client, the following code is common:

  1. if(usetls){
  2. if((tls_cfg = tls_config_new())== NULL)
  3. errx(1,"unable to allocate TLS config");
  4. if(Rflag&& tls_config_set_ca_file(tls_cfg,Rflag)==-1)
  5. errx(1,"%s", tls_config_error(tls_cfg));
  6. if(Cflag&& tls_config_set_cert_file(tls_cfg,Cflag)==-1)
  7. errx(1,"%s", tls_config_error(tls_cfg));
  8. if(Kflag&& tls_config_set_key_file(tls_cfg,Kflag)==-1)
  9. errx(1,"%s", tls_config_error(tls_cfg));
  10. if(oflag && tls_config_set_ocsp_staple_file(tls_cfg, oflag)==-1)
  11. errx(1,"%s", tls_config_error(tls_cfg));
  12. if(tls_config_parse_protocols(&protocols, tls_protocols)==-1)
  13. errx(1,"invalid TLS protocols `%s'", tls_protocols);
  14. ......
  15. }

tls_config_new() returns a new default configuration object. For other options and settings, I won’t elaborate them here.

Then, as SSL/TLS server, tls_server() should be called and return a context object:

  1. ......
  2. if((tls_ctx = tls_server())== NULL)
  3. errx(1,"tls server creation failed");
  4. ......

Similarly, client invokes tls_client():

  1. ......
  2. if((tls_ctx = tls_client())== NULL)
  3. errx(1,"tls client creation failed");
  4. ......

Both server and client should associate configuration and context objects:

  1. if(tls_configure(tls_ctx, tls_cfg)==-1)
  2. errx(1,"tls configuration failed (%s)",
  3. tls_error(tls_ctx));

(2) Next step is associating exist socket with SSL/TLS context object:
a) Server uses tls_accept_socket():

  1. ......
  2. if(tls_accept_socket(tls_ctx,&tls_cctx, connfd)==-1){
  3. warnx("tls accept failed (%s)", tls_error(tls_ctx));
  4. }
  5. ......

b) Client uses tls_connect_socket():

  1. ......
  2. if(tls_connect_socket(tls_ctx, s,
  3. tls_expectname ? tls_expectname : host)==-1){
  4. errx(1,"tls connection failed (%s)",
  5. tls_error(tls_ctx));
  6. }
  7. ......

(3) Read & Write:
After SSL/TLS connection is established, you can use tls_read() and tls_write to receive and send data. Different with read() and write() system calls, tls_read() and tls_write will process 2 more return values: TLS_WANT_POLLIN and TLS_WANT_POLLOUT. E.g.:

  1. ......
  2. ret = fillbuf(pfd[POLL_STDIN].fd, stdinbuf,
  3. &stdinbufpos, NULL);
  4. if(ret == TLS_WANT_POLLIN)
  5. pfd[POLL_STDIN].events = POLLIN;
  6. elseif(ret == TLS_WANT_POLLOUT)
  7. pfd[POLL_STDIN].events = POLLOUT;
  8. ......