tio_tls.c - sacc - sacc (saccomys): simple gopher client.
(DIR) Log
(DIR) Files
(DIR) Refs
(DIR) LICENSE
---
tio_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;