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 9dec9b200c1bddd6d12f4aa6b43e2c329f513235
Quentin Rameau <quinq@fifth.space>
2022-10-15 12:50:09 +0200

tls: Handle local certificate path

Although I believe that every computer should have a user "quinq",
use "~/share/sacc/cert" path by default for looking up certificates.

Also, let the user override this default with the environment variable
SACC_CERT_DIR.

 io.h     |   1 +
 io_clr.c |   7 +++
 io_tls.c | 154 ++++++++++++++++++++++++++++++++++++++++++++++++++-------------
 sacc.c   |   1 +
 4 files changed, 132 insertions(+), 31 deletions(-)
diff --git a/io.h b/io.h
index a705355..ae92d8a 100644
--- a/io.h
+++ b/io.h
@@ -14,6 +14,7 @@ struct cnx {
 
 extern int tls;
 
+extern int (*iosetup)(void);
 extern int (*ioclose)(struct cnx *);
 extern int (*ioconnect)(struct cnx *, struct addrinfo *, const char *);
 extern void (*ioconnerr)(struct cnx *, const char *, const char *, int);
diff --git a/io_clr.c b/io_clr.c
index 142f6a5..07277af 100644
--- a/io_clr.c
+++ b/io_clr.c
@@ -7,6 +7,12 @@
 #include "common.h"
 #include "io.h"
 
+static int
+setup_clr(void)
+{
+	return 0;
+}
+
 static int
 close_clr(struct cnx *c)
 {
@@ -55,6 +61,7 @@ write_clr(struct cnx *c, void *buf, size_t bs)
 	return write(c->sock, buf, bs);
 }
 
+int (*iosetup)(void) = setup_clr;
 int (*ioclose)(struct cnx *) = close_clr;
 int (*ioconnect)(struct cnx *, struct addrinfo *, const char *) = connect_clr;
 void (*ioconnerr)(struct cnx *, const char *, const char *, int) = connerr_clr;
diff --git a/io_tls.c b/io_tls.c
index 3fe7904..b95368e 100644
--- a/io_tls.c
+++ b/io_tls.c
@@ -1,4 +1,6 @@
+#include <errno.h>
 #include <limits.h>
+#include <pwd.h>
 #include <stdio.h>
 #include <stdlib.h>
 #include <string.h>
@@ -6,6 +8,7 @@
 #include <netdb.h>
 
 #include <sys/socket.h>
+#include <sys/stat.h>
 
 #include <tls.h>
 
@@ -16,8 +19,71 @@
 #define TLS_ON  1
 #define TLS_PEM 2
 
+struct pem {
+	char path[PATH_MAX];
+	char *dir;
+	char *cert;
+	size_t certln;
+};
+
 int tls;
 
+static struct pem pem = { .dir = "share/sacc/cert" };
+
+static int
+mkpath(char *path, mode_t mode)
+{
+	char *s;
+	int r;
+
+	for (s = path+1; (s = strchr(s, '/')) != NULL; ++s) {
+		s[0] = '\0';
+		errno = 0;
+		r = mkdir(path, mode);
+		s[0] = '/';
+		if (r == -1 && errno != EEXIST)
+			return -1;
+	};
+	if (mkdir(path, S_IRWXU) == -1 && errno != EEXIST)
+		return -1;
+	return 0;
+}
+
+static int
+setup_tls(void)
+{
+	struct passwd *pw;
+	char *p;
+	int n;
+
+	if ((p = getenv("SACC_CERT_DIR")) != NULL) {
+		n = snprintf(pem.path, sizeof(pem.path), "%s/", p);
+		if (n < 0 || (unsigned)n >= sizeof(pem.path)) {
+			diag("PEM path too long: %s/", p);
+			return -1;
+		}
+	} else {
+		if ((pw = getpwuid(geteuid())) == NULL)
+			return -1;
+		n = snprintf(pem.path, sizeof(pem.path), "%s/%s/",
+		             pw->pw_dir, pem.dir);
+		if (n < 0 || (unsigned)n >= sizeof(pem.path)) {
+			diag("PEM path too long: %s/%s/", pw->pw_dir, pem.dir);
+			return -1;
+		}
+	}
+
+	if (mkpath(pem.path, S_IRWXU) == -1) {
+		diag("Can't create cert dir: %s: %s",
+		     pem.path, strerror(errno));
+	} else {
+		pem.cert = pem.path + n;
+		pem.certln = pem.cert - pem.path;
+	}
+
+	return 0;
+}
+
 static int
 close_tls(struct cnx *c)
 {
@@ -41,6 +107,8 @@ savepem(struct tls *t, char *path)
 	const char *s;
 	size_t ln;
 
+	if (path == NULL)
+		return -1;
 	if ((s = tls_peer_cert_chain_pem(t, &ln)) == NULL)
 		return -1;
 	if ((f = fopen(path, "w")) == NULL)
@@ -52,55 +120,77 @@ savepem(struct tls *t, char *path)
 	return 0;
 }
 
-static int
-connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
+static char *
+conftls(struct tls *t, const char *host)
 {
-	char pempath[PATH_MAX];
-	struct tls *t;
 	struct tls_config *tc;
-	char *s;
-	int r;
+	char *p;
+	int n;
 
-	c->tls = NULL;
 	tc = NULL;
-	s = NULL;
-	r = -1;
+	p = NULL;
 
-	if (connect(c->sock, ai->ai_addr, ai->ai_addrlen) == -1)
-		return -1;
+	if (pem.cert == NULL)
+		return NULL;
 
-	if (!tls)
-		return 0;
-
-	if ((t = tls_client()) == NULL)
-		return -1;
+	n = snprintf(pem.cert, pem.certln, host);
+	if (n < 0 || (unsigned)n >= pem.certln) {
+		diag("PEM path too long: %s/%s", pem.cert, host);
+		return NULL;
+	}
 
-/* XXX: construct path from getuid home path */
-	snprintf(pempath, sizeof(pempath), "/home/quinq/share/sacc/%s.pem", host);
 	switch (tls) {
 	case TLS_ON:
 		/* check if there is a local certificate for target */
-		if (access(pempath, R_OK) == 0) {
+		if (access(pem.path, R_OK) == 0) {
 			if ((tc = tls_config_new()) == NULL)
-				goto end;
-			if (tls_config_set_ca_file(tc, pempath) == -1)
+				return NULL;
+			if (tls_config_set_ca_file(tc, pem.path) == -1)
 				goto end;
 			if (tls_configure(t, tc) == -1)
 				goto end;
+			p = pem.path;
 		}
 		break;
 	case TLS_PEM:
 		/* save target certificate to file */
 		if ((tc = tls_config_new()) == NULL)
-			goto end;
+			return NULL;
 		tls_config_insecure_noverifycert(tc);
 		if (tls_configure(t, tc) == -1)
 			goto end;
+		p = pem.path;
 		break;
 	}
+end:
+	tls_config_free(tc);
+	return p;
+}
+
+static int
+connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
+{
+	struct tls *t;
+	char *s, *pempath;
+	int r;
+
+	c->tls = NULL;
+	s = NULL;
+	r = CONN_ERROR;
+
+	if (connect(c->sock, ai->ai_addr, ai->ai_addrlen) == -1)
+		return r;
+
+	if (!tls)
+		return CONN_VALID;
+
+	if ((t = tls_client()) == NULL)
+		return r;
+
+	pempath = conftls(t, host);
 
 	if (tls_connect_socket(t, c->sock, host) == -1)
-		return -1;
+		goto end;
 
 	do {
 		r = tls_handshake(t);
@@ -120,13 +210,15 @@ connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
 		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 = TLS_PEM;
-			r = CONN_RETRY;
-			goto end;
+		if (pem.cert) {
+			s = uiprompt("Save certificate locally and retry? [yN]: ");
+			switch (*s) {
+			case 'Y':
+			case 'y':
+				tls = TLS_PEM;
+				r = CONN_RETRY;
+				goto end;
+			}
 		}
 
 		s = uiprompt("Retry on cleartext? [Yn]: ");
@@ -143,7 +235,6 @@ connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
 	}
 end:
 	free(s);
-	tls_config_free(tc);
 	if (r != CONN_VALID)
 		tls_free(t);
 
@@ -219,6 +310,7 @@ write_tls(struct cnx *c, void *buf, size_t bs)
 	return n;
 }
 
+int (*iosetup)(void) = setup_tls;
 int (*ioclose)(struct cnx *) = close_tls;
 int (*ioconnect)(struct cnx *, struct addrinfo *, const char *) = connect_tls;
 void (*ioconnerr)(struct cnx *, const char *, const char *, int) = connerr_tls;
diff --git a/sacc.c b/sacc.c
index 21ca9e0..e76c42d 100644
--- a/sacc.c
+++ b/sacc.c
@@ -1113,6 +1113,7 @@ setup(void)
 	} else {
 		diag = stddiag;
 	}
+	iosetup();
 }
 
 int