URI: 
       io_tls.c - 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
       ---
       io_tls.c (6002B)
       ---
            1 #include <errno.h>
            2 #include <limits.h>
            3 #include <pwd.h>
            4 #include <stdio.h>
            5 #include <stdlib.h>
            6 #include <string.h>
            7 #include <unistd.h>
            8 #include <netdb.h>
            9 
           10 #include <sys/socket.h>
           11 #include <sys/stat.h>
           12 
           13 #include <tls.h>
           14 
           15 #include "common.h"
           16 #include "io.h"
           17 
           18 #define TLS_OFF 0
           19 #define TLS_ON  1
           20 #define TLS_PEM 2
           21 
           22 struct pem {
           23         char path[PATH_MAX];
           24         char *dir;
           25         char *cert;
           26         size_t certsz;
           27 };
           28 
           29 int tls;
           30 
           31 static struct pem pem = { .dir = ".share/sacc/cert" };
           32 
           33 static int
           34 mkpath(char *path, mode_t mode)
           35 {
           36         char *s;
           37         int r;
           38 
           39         for (s = path+1; (s = strchr(s, '/')) != NULL; ++s) {
           40                 s[0] = '\0';
           41                 errno = 0;
           42                 r = mkdir(path, mode);
           43                 s[0] = '/';
           44                 if (r == -1 && errno != EEXIST)
           45                         return -1;
           46         };
           47         if (mkdir(path, S_IRWXU) == -1 && errno != EEXIST)
           48                 return -1;
           49         return 0;
           50 }
           51 
           52 static int
           53 setup_tls(void)
           54 {
           55         struct passwd *pw;
           56         char *p;
           57         int n;
           58 
           59         if ((p = getenv("SACC_CERT_DIR")) != NULL) {
           60                 n = snprintf(pem.path, sizeof(pem.path), "%s/", p);
           61                 if (n < 0 || (unsigned)n >= sizeof(pem.path)) {
           62                         diag("PEM path too long: %s/", p);
           63                         return -1;
           64                 }
           65         } else {
           66                 if ((pw = getpwuid(geteuid())) == NULL)
           67                         return -1;
           68                 n = snprintf(pem.path, sizeof(pem.path), "%s/%s/",
           69                              pw->pw_dir, pem.dir);
           70                 if (n < 0 || (unsigned)n >= sizeof(pem.path)) {
           71                         diag("PEM path too long: %s/%s/", pw->pw_dir, pem.dir);
           72                         return -1;
           73                 }
           74         }
           75 
           76         if (mkpath(pem.path, S_IRWXU) == -1) {
           77                 diag("Can't create cert dir: %s: %s",
           78                      pem.path, strerror(errno));
           79         } else {
           80                 pem.cert = pem.path + n;
           81                 pem.certsz = sizeof(pem.path) - n;
           82         }
           83 
           84         return 0;
           85 }
           86 
           87 static int
           88 close_tls(struct cnx *c)
           89 {
           90         int r;
           91 
           92         if (tls != TLS_OFF && c->tls) {
           93                 do {
           94                         r = tls_close(c->tls);
           95                 } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
           96 
           97                 tls_free(c->tls);
           98         }
           99 
          100         return close(c->sock);
          101 }
          102 
          103 static int
          104 savepem(struct tls *t, char *path)
          105 {
          106         FILE *f;
          107         const char *s;
          108         size_t ln;
          109         int e = 0;
          110 
          111         if (path == NULL)
          112                 return -1;
          113         if ((s = tls_peer_cert_chain_pem(t, &ln)) == NULL)
          114                 return -1;
          115         if ((f = fopen(path, "w")) == NULL)
          116                 return -1;
          117 
          118         while (ln > 0)
          119                 ln = fwrite(s, 1, ln, f);
          120 
          121         if (ferror(f))
          122                 e = -1;
          123         if (fclose(f) != 0)
          124                 e = -1;
          125         if (e == -1)
          126                 unlink(path);
          127 
          128         return e;
          129 }
          130 
          131 static char *
          132 conftls(struct tls *t, const char *host)
          133 {
          134         struct tls_config *tc;
          135         char *p;
          136         int n;
          137 
          138         tc = NULL;
          139         p = NULL;
          140 
          141         if (pem.cert == NULL)
          142                 return NULL;
          143 
          144         n = snprintf(pem.cert, pem.certsz, "%s", host);
          145         if (n < 0 || (unsigned)n >= pem.certsz) {
          146                 diag("PEM path too long: %s/%s", pem.cert, host);
          147                 return NULL;
          148         }
          149 
          150         switch (tls) {
          151         case TLS_ON:
          152                 /* check if there is a local certificate for target */
          153                 if (access(pem.path, R_OK) == 0) {
          154                         if ((tc = tls_config_new()) == NULL)
          155                                 return NULL;
          156                         if (tls_config_set_ca_file(tc, pem.path) == -1)
          157                                 goto end;
          158                         if (tls_configure(t, tc) == -1)
          159                                 goto end;
          160                         p = pem.path;
          161                 }
          162                 break;
          163         case TLS_PEM:
          164                 /* save target certificate to file */
          165                 if ((tc = tls_config_new()) == NULL)
          166                         return NULL;
          167                 tls_config_insecure_noverifycert(tc);
          168                 if (tls_configure(t, tc) == -1)
          169                         goto end;
          170                 p = pem.path;
          171                 break;
          172         }
          173 end:
          174         tls_config_free(tc);
          175         return p;
          176 }
          177 
          178 static int
          179 connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
          180 {
          181         struct tls *t;
          182         char *s, *pempath;
          183         int r;
          184 
          185         c->tls = NULL;
          186         s = NULL;
          187         r = CONN_ERROR;
          188 
          189         if (connect(c->sock, ai->ai_addr, ai->ai_addrlen) == -1)
          190                 return r;
          191 
          192         if (!tls)
          193                 return CONN_VALID;
          194 
          195         if ((t = tls_client()) == NULL)
          196                 return r;
          197 
          198         pempath = conftls(t, host);
          199 
          200         if (tls_connect_socket(t, c->sock, host) == -1)
          201                 goto end;
          202 
          203         do {
          204                 r = tls_handshake(t);
          205         } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
          206 
          207         if (r == 0) {
          208                 switch (tls) {
          209                 case TLS_ON:
          210                         c->tls = t;
          211                         break;
          212                 case TLS_PEM:
          213                         r = savepem(t, pempath) == 0 ? CONN_RETRY : CONN_ERROR;
          214                         tls = TLS_ON;
          215                         break;
          216                 }
          217         } else {
          218                 diag("Can't establish TLS with \"%s\": %s",
          219                      host, tls_error(t));
          220 
          221                 if (!interactive) {
          222                         r = CONN_ABORT;
          223                         goto end;
          224                 }
          225 
          226                 if (pem.cert) {
          227                         s = uiprompt("Save certificate locally and retry? [yN]: ");
          228                         switch (*s) {
          229                         case 'Y':
          230                         case 'y':
          231                                 tls = TLS_PEM;
          232                                 r = CONN_RETRY;
          233                                 goto end;
          234                         }
          235                 }
          236 
          237                 s = uiprompt("Retry on cleartext? [Yn]: ");
          238                 switch (*s) {
          239                 case 'Y':
          240                 case 'y':
          241                 case '\0':
          242                         tls = TLS_OFF;
          243                         r = CONN_RETRY;
          244                         break;
          245                 default:
          246                         r = CONN_ABORT;
          247                 }
          248         }
          249 end:
          250         free(s);
          251         if (r != CONN_VALID)
          252                 tls_free(t);
          253 
          254         return r;
          255 }
          256 
          257 static void
          258 connerr_tls(struct cnx *c, const char *host, const char *port, int err)
          259 {
          260         if (c->sock == -1) {
          261                 diag("Can't open socket: %s", strerror(err));
          262         } else {
          263                 if (tls != TLS_OFF && c->tls) {
          264                         diag("Can't establish TLS with \"%s\": %s",
          265                              host, tls_error(c->tls));
          266                 } else {
          267                         diag("Can't connect to: %s:%s: %s", host, port,
          268                              strerror(err));
          269                 }
          270         }
          271 }
          272 
          273 static char *
          274 parseurl_tls(char *url)
          275 {
          276         char *p;
          277 
          278         if (p = strstr(url, "://")) {
          279                 if (!strncmp(url, "gopher", p - url)) {
          280                         if (tls)
          281                                 diag("Switching from gophers to gopher");
          282                         tls = TLS_OFF;
          283                 } else if (!strncmp(url, "gophers", p - url)) {
          284                         tls = TLS_ON;
          285                 } else {
          286                         die("Protocol not supported: %.*s", p - url, url);
          287                 }
          288                 url = p + 3;
          289         }
          290 
          291         return url;
          292 }
          293 
          294 static ssize_t
          295 read_tls(struct cnx *c, void *buf, size_t bs)
          296 {
          297         ssize_t n;
          298 
          299         if (tls != TLS_OFF && c->tls) {
          300                 do {
          301                         n = tls_read(c->tls, buf, bs);
          302                 } while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
          303         } else {
          304                 n = read(c->sock, buf, bs);
          305         }
          306 
          307         return n;
          308 }
          309 
          310 static ssize_t
          311 write_tls(struct cnx *c, void *buf, size_t bs)
          312 {
          313         ssize_t n;
          314 
          315         if (tls) {
          316                 do {
          317                         n = tls_write(c->tls, buf, bs);
          318                 } while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
          319         } else {
          320                 n = write(c->sock, buf, bs);
          321         }
          322 
          323         return n;
          324 }
          325 
          326 int (*iosetup)(void) = setup_tls;
          327 int (*ioclose)(struct cnx *) = close_tls;
          328 int (*ioconnect)(struct cnx *, struct addrinfo *, const char *) = connect_tls;
          329 void (*ioconnerr)(struct cnx *, const char *, const char *, int) = connerr_tls;
          330 char *(*ioparseurl)(char *) = parseurl_tls;
          331 ssize_t (*ioread)(struct cnx *, void *, size_t) = read_tls;
          332 ssize_t (*iowrite)(struct cnx *, void *, size_t) = write_tls;