URI: 
       tls: Handle local certificate path - sacc - sacc(omys), simple console gopher client
  HTML git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/sacc/
   DIR Log
   DIR Files
   DIR Refs
   DIR Tags
   DIR LICENSE
       ---
   DIR commit 9dec9b200c1bddd6d12f4aa6b43e2c329f513235
   DIR parent 392dcb24732c88f500266b923bafdda8eb631220
  HTML Author: Quentin Rameau <quinq@fifth.space>
       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(-)
       ---
   DIR 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);
   DIR 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;
   DIR 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", 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;
   DIR diff --git a/sacc.c b/sacc.c
       @@ -1113,6 +1113,7 @@ setup(void)
                } else {
                        diag = stddiag;
                }
       +        iosetup();
        }
        
        int