Introduction
Introduction Statistics Contact Development Disclaimer Help
io_tls.c - sacc - sacc - sacc(omys), simple console gopher client (config)
git clone git://git.codemadness.org/sacc
Log
Files
Refs
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, pe…
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…
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…
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 *) = connec…
329 void (*ioconnerr)(struct cnx *, const char *, const char *, int) = conne…
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;
You are viewing proxied material from codemadness.org. The copyright of proxied material belongs to its original authors. Any comments or complaints in relation to proxied material should be directed to the original authors of the content concerned. Please see the disclaimer for more details.