io_tls.c - sacc - sacc(omys), simple console gopher client
 (HTM) git clone git://bitreich.org/sacc/ git://enlrupgkhuxnvlhsf6lc3fziv5h2hhfrinws65d7roiv6bfj7d652fid.onion/sacc/
 (DIR) Log
 (DIR) Files
 (DIR) Refs
 (DIR) Tags
 (DIR) LICENSE
       ---
       io_tls.c (5921B)
       ---
            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 
          110         if (path == NULL)
          111                 return -1;
          112         if ((s = tls_peer_cert_chain_pem(t, &ln)) == NULL)
          113                 return -1;
          114         if ((f = fopen(path, "w")) == NULL)
          115                 return -1;
          116         fprintf(f, "%.*s\n", ln, s);
          117         if (fclose(f) != 0)
          118                 return -1;
          119 
          120         return 0;
          121 }
          122 
          123 static char *
          124 conftls(struct tls *t, const char *host)
          125 {
          126         struct tls_config *tc;
          127         char *p;
          128         int n;
          129 
          130         tc = NULL;
          131         p = NULL;
          132 
          133         if (pem.cert == NULL)
          134                 return NULL;
          135 
          136         n = snprintf(pem.cert, pem.certsz, "%s", host);
          137         if (n < 0 || (unsigned)n >= pem.certsz) {
          138                 diag("PEM path too long: %s/%s", pem.cert, host);
          139                 return NULL;
          140         }
          141 
          142         switch (tls) {
          143         case TLS_ON:
          144                 /* check if there is a local certificate for target */
          145                 if (access(pem.path, R_OK) == 0) {
          146                         if ((tc = tls_config_new()) == NULL)
          147                                 return NULL;
          148                         if (tls_config_set_ca_file(tc, pem.path) == -1)
          149                                 goto end;
          150                         if (tls_configure(t, tc) == -1)
          151                                 goto end;
          152                         p = pem.path;
          153                 }
          154                 break;
          155         case TLS_PEM:
          156                 /* save target certificate to file */
          157                 if ((tc = tls_config_new()) == NULL)
          158                         return NULL;
          159                 tls_config_insecure_noverifycert(tc);
          160                 if (tls_configure(t, tc) == -1)
          161                         goto end;
          162                 p = pem.path;
          163                 break;
          164         }
          165 end:
          166         tls_config_free(tc);
          167         return p;
          168 }
          169 
          170 static int
          171 connect_tls(struct cnx *c, struct addrinfo *ai, const char *host)
          172 {
          173         struct tls *t;
          174         char *s, *pempath;
          175         int r;
          176 
          177         c->tls = NULL;
          178         s = NULL;
          179         r = CONN_ERROR;
          180 
          181         if (connect(c->sock, ai->ai_addr, ai->ai_addrlen) == -1)
          182                 return r;
          183 
          184         if (!tls)
          185                 return CONN_VALID;
          186 
          187         if ((t = tls_client()) == NULL)
          188                 return r;
          189 
          190         pempath = conftls(t, host);
          191 
          192         if (tls_connect_socket(t, c->sock, host) == -1)
          193                 goto end;
          194 
          195         do {
          196                 r = tls_handshake(t);
          197         } while (r == TLS_WANT_POLLIN || r == TLS_WANT_POLLOUT);
          198 
          199         if (r == 0) {
          200                 switch (tls) {
          201                 case TLS_ON:
          202                         c->tls = t;
          203                         break;
          204                 case TLS_PEM:
          205                         r = savepem(t, pempath) == 0 ? CONN_RETRY : CONN_ERROR;
          206                         tls = TLS_ON;
          207                         break;
          208                 }
          209         } else {
          210                 diag("Can't establish TLS with \"%s\": %s",
          211                      host, tls_error(t));
          212 
          213                 if (!interactive) {
          214                         r = CONN_ABORT;
          215                         goto end;
          216                 }
          217 
          218                 if (pem.cert) {
          219                         s = uiprompt("Save certificate locally and retry? [yN]: ");
          220                         switch (*s) {
          221                         case 'Y':
          222                         case 'y':
          223                                 tls = TLS_PEM;
          224                                 r = CONN_RETRY;
          225                                 goto end;
          226                         }
          227                 }
          228 
          229                 s = uiprompt("Retry on cleartext? [Yn]: ");
          230                 switch (*s) {
          231                 case 'Y':
          232                 case 'y':
          233                 case '\0':
          234                         tls = TLS_OFF;
          235                         r = CONN_RETRY;
          236                         break;
          237                 default:
          238                         r = CONN_ABORT;
          239                 }
          240         }
          241 end:
          242         free(s);
          243         if (r != CONN_VALID)
          244                 tls_free(t);
          245 
          246         return r;
          247 }
          248 
          249 static void
          250 connerr_tls(struct cnx *c, const char *host, const char *port, int err)
          251 {
          252         if (c->sock == -1) {
          253                 diag("Can't open socket: %s", strerror(err));
          254         } else {
          255                 if (tls != TLS_OFF && c->tls) {
          256                         diag("Can't establish TLS with \"%s\": %s",
          257                              host, tls_error(c->tls));
          258                 } else {
          259                         diag("Can't connect to: %s:%s: %s", host, port,
          260                              strerror(err));
          261                 }
          262         }
          263 }
          264 
          265 static char *
          266 parseurl_tls(char *url)
          267 {
          268         char *p;
          269 
          270         if (p = strstr(url, "://")) {
          271                 if (!strncmp(url, "gopher", p - url)) {
          272                         if (tls)
          273                                 diag("Switching from gophers to gopher");
          274                         tls = TLS_OFF;
          275                 } else if (!strncmp(url, "gophers", p - url)) {
          276                         tls = TLS_ON;
          277                 } else {
          278                         die("Protocol not supported: %.*s", p - url, url);
          279                 }
          280                 url = p + 3;
          281         }
          282 
          283         return url;
          284 }
          285 
          286 static ssize_t
          287 read_tls(struct cnx *c, void *buf, size_t bs)
          288 {
          289         ssize_t n;
          290 
          291         if (tls != TLS_OFF && c->tls) {
          292                 do {
          293                         n = tls_read(c->tls, buf, bs);
          294                 } while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
          295         } else {
          296                 n = read(c->sock, buf, bs);
          297         }
          298 
          299         return n;
          300 }
          301 
          302 static ssize_t
          303 write_tls(struct cnx *c, void *buf, size_t bs)
          304 {
          305         ssize_t n;
          306 
          307         if (tls) {
          308                 do {
          309                         n = tls_write(c->tls, buf, bs);
          310                 } while (n == TLS_WANT_POLLIN || n == TLS_WANT_POLLOUT);
          311         } else {
          312                 n = write(c->sock, buf, bs);
          313         }
          314 
          315         return n;
          316 }
          317 
          318 int (*iosetup)(void) = setup_tls;
          319 int (*ioclose)(struct cnx *) = close_tls;
          320 int (*ioconnect)(struct cnx *, struct addrinfo *, const char *) = connect_tls;
          321 void (*ioconnerr)(struct cnx *, const char *, const char *, int) = connerr_tls;
          322 char *(*ioparseurl)(char *) = parseurl_tls;
          323 ssize_t (*ioread)(struct cnx *, void *, size_t) = read_tls;
          324 ssize_t (*iowrite)(struct cnx *, void *, size_t) = write_tls;