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;