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.1 200 OK
  6. Date: Fri, 21 Sep 2018 08: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. else if (ret == TLS_WANT_POLLOUT)
  7. pfd[POLL_STDIN].events = POLLOUT;
  8. ......