Recently Written · git

sacc

Mirror of git://bitreich.org/sacc, the sacc gopher client from bitreich.org.

git clone https://github.com/equwal/sacc

Log | Files | Refs


commit e8930420e7c31a3cea0484314bd38aba5197fa39
Quentin Rameau <quinq@fifth.space>
2022-09-20 23:04:00 +0200

tls: Add support for TOFU (Trusted On First Use)

 io_tls.c | 77 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
 sacc.c   |  2 +-
 2 files changed, 76 insertions(+), 3 deletions(-)
diff --git a/io_tls.c b/io_tls.c
index 76f7334..7d99f44 100644
--- a/io_tls.c
+++ b/io_tls.c
@@ -1,3 +1,5 @@
+#include <limits.h>
+#include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
 #include <unistd.h>
@@ -28,14 +30,37 @@ close_tls(struct cnx *c)
 	return close(c->sock);
 }
 
+static int
+savepem(struct tls *t, char *path)
+{
+	FILE *f;
+	const char *s;
+	size_t ln;
+
+	if ((s = tls_peer_cert_chain_pem(t, &ln)) == NULL)
+		return -1;
+	if ((f = fopen(path, "w")) == NULL)
+		return -1;
+	fprintf(f, "%.*s\n", ln, s);
+	if (fclose(f) != 0)
+		return -1;
+
+	return 0;
+}
+
 static int
 connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
 {
+	char pempath[PATH_MAX];
 	struct tls *t;
+	struct tls_config *tc;
 	char *s;
 	int r;
 
 	c->tls = NULL;
+	tc = NULL;
+	s = NULL;
+	r = -1;
 
 	if (connect(c->sock, ai->ai_addr, ai->ai_addrlen) == -1)
 		return -1;
@@ -45,16 +70,60 @@ connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
 
 	if ((t = tls_client()) == NULL)
 		return -1;
+
+	snprintf(pempath, sizeof(pempath), "/home/quinq/share/sacc/%s.pem", host);
+	switch (tls) {
+	case 1:
+		/* check if there is a local certificate for target */
+		if (access(pempath, R_OK) == 0) {
+			if ((tc = tls_config_new()) == NULL)
+				goto end;
+			if (tls_config_set_ca_file(tc, pempath) == -1)
+				goto end;
+			if (tls_configure(t, tc) == -1)
+				goto end;
+		}
+		break;
+	case 2:
+		/* save target certificate to file */
+		if ((tc = tls_config_new()) == NULL)
+			goto end;
+		tls_config_insecure_noverifycert(tc);
+		if (tls_configure(t, tc) == -1)
+			goto end;
+		break;
+	}
+
 	if (tls_connect_socket(t, c->sock, host) == -1)
 		return -1;
+
 	do {
 		r = tls_handshake(t);
 	} while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
+
 	if (r == 0) {
-		c->tls = t;
+		switch (tls) {
+		case 1:
+			c->tls = t;
+			break;
+		case 2:
+			r = savepem(t, pempath) == 0 ? -2 : -1;
+			tls = 1;
+			break;
+		}
 	} else {
 		diag("Can't establish TLS with \"%s\": %s",
 		     host, tls_error(t));
+
+		s = uiprompt("Save certificate locally and retry? [yN]: ");
+		switch (*s) {
+		case 'Y':
+		case 'y':
+			tls = 2;
+			r = -2;
+			goto end;
+		}
+
 		s = uiprompt("Retry on cleartext? [Yn]: ");
 		switch (*s) {
 		case 'Y':
@@ -66,8 +135,12 @@ connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
 		default:
 			r = -3;
 		}
-		free(s);
 	}
+end:
+	free(s);
+	tls_config_free(tc);
+	if (r != 0)
+		tls_free(t);
 
 	return r;
 }
diff --git a/sacc.c b/sacc.c
index 2f57daa..7cdcb08 100644
--- a/sacc.c
+++ b/sacc.c
@@ -581,7 +581,7 @@ connectto(const char *host, const char *port, struct cnx *c)
 				err = errno;
 				ioclose(c);
 			}
-		/* retry on cleartext */
+		/* retry */
 		} while (r == -2);
 	}