tls: Handle local certificate path - sacc - sacc(omys), simple console gopher c… | |
git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65… | |
Log | |
Files | |
Refs | |
Tags | |
LICENSE | |
--- | |
commit 9dec9b200c1bddd6d12f4aa6b43e2c329f513235 | |
parent 392dcb24732c88f500266b923bafdda8eb631220 | |
Author: Quentin Rameau <[email protected]> | |
Date: Sat, 15 Oct 2022 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. | |
Diffstat: | |
M io.h | 1 + | |
M io_clr.c | 7 +++++++ | |
M io_tls.c | 154 ++++++++++++++++++++++++-----… | |
M sacc.c | 1 + | |
4 files changed, 132 insertions(+), 31 deletions(-) | |
--- | |
diff --git 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 | |
@@ -8,6 +8,12 @@ | |
#include "io.h" | |
static int | |
+setup_clr(void) | |
+{ | |
+ return 0; | |
+} | |
+ | |
+static int | |
close_clr(struct cnx *c) | |
{ | |
return close(c->sock); | |
@@ -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 | |
@@ -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", ho… | |
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 cha… | |
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 … | |
} | |
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 | |
@@ -1113,6 +1113,7 @@ setup(void) | |
} else { | |
diag = stddiag; | |
} | |
+ iosetup(); | |
} | |
int |