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;