/
ssl.c
1040 lines (918 loc) · 26.7 KB
1
/*
2
* OpenConnect (SSL + DTLS) VPN client
3
*
4
* Copyright © 2008-2015 Intel Corporation.
5
*
6
7
8
* Author: David Woodhouse <dwmw2@infradead.org>
*
* This program is free software; you can redistribute it and/or
9
* modify it under the terms of the GNU Lesser General Public License
10
* version 2.1, as published by the Free Software Foundation.
11
*
12
* This program is distributed in the hope that it will be useful, but
13
14
15
* WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Lesser General Public License for more details.
16
*/
17
18
19
#include <config.h>
20
#include <sys/types.h>
21
#include <sys/stat.h>
22
#include <unistd.h>
23
#include <fcntl.h>
24
#include <string.h>
25
#include <stdio.h>
26
#include <errno.h>
27
#include <stdlib.h>
28
#include <stdarg.h>
29
#include <time.h>
30
#if defined(__linux__) || defined(__ANDROID__)
31
#include <sys/vfs.h>
32
#elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__APPLE__)
33
34
#include <sys/param.h>
#include <sys/mount.h>
35
#elif defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
36
#include <sys/statvfs.h>
37
#elif defined(__GNU__)
38
#include <sys/statfs.h>
39
#endif
40
41
#include "openconnect-internal.h"
42
43
44
45
46
#ifdef ANDROID_KEYSTORE
#include <sys/un.h>
#endif
47
48
49
50
51
/* OSX < 1.6 doesn't have AI_NUMERICSERV */
#ifndef AI_NUMERICSERV
#define AI_NUMERICSERV 0
#endif
52
53
54
55
56
57
58
59
static inline int connect_pending()
{
#ifdef _WIN32
return WSAGetLastError() == WSAEWOULDBLOCK;
#else
return errno == EINPROGRESS;
#endif
}
60
61
62
63
64
65
66
/* Windows is interminably horrid, and has disjoint errno spaces.
* So if we return a positive value, that's a WSA Error and should
* be handled with openconnect__win32_strerror(). But if we return a
* negative value, that's a normal errno and should be handled with
* strerror(). No, you can't just pass the latter value (negated) to
* openconnect__win32_strerror() because it gives nonsense results. */
67
68
69
70
71
static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
const struct sockaddr *addr, socklen_t addrlen)
{
struct sockaddr_storage peer;
socklen_t peerlen = sizeof(peer);
72
fd_set wr_set, rd_set, ex_set;
73
int maxfd = sockfd;
74
int err;
75
76
set_sock_nonblock(sockfd);
77
78
if (vpninfo->protect_socket)
vpninfo->protect_socket(vpninfo->cbdata, sockfd);
79
80
81
82
83
84
85
86
if (connect(sockfd, addr, addrlen) < 0 && !connect_pending()) {
#ifdef _WIN32
return WSAGetLastError();
#else
return -errno;
#endif
}
87
88
89
90
do {
FD_ZERO(&wr_set);
FD_ZERO(&rd_set);
91
FD_ZERO(&ex_set);
92
FD_SET(sockfd, &wr_set);
93
94
95
#ifdef _WIN32 /* Windows indicates failure this way, not in wr_set */
FD_SET(sockfd, &ex_set);
#endif
96
cmd_fd_set(vpninfo, &rd_set, &maxfd);
97
select(maxfd + 1, &rd_set, &wr_set, &ex_set, NULL);
98
99
if (is_cancel_pending(vpninfo, &rd_set)) {
vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n"));
100
return -EINTR;
101
}
102
103
} while (!FD_ISSET(sockfd, &wr_set) && !FD_ISSET(sockfd, &ex_set) &&
!vpninfo->got_pause_cmd);
104
105
106
/* Check whether connect() succeeded or failed by using
getpeername(). See http://cr.yp.to/docs/connect.html */
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
if (!getpeername(sockfd, (void *)&peer, &peerlen))
return 0;
#ifdef _WIN32 /* On Windows, use getsockopt() to determine the error.
* We don't ddo this on Windows because it just reports
* -ENOTCONN, which we already knew. */
err = WSAGetLastError();
if (err == WSAENOTCONN) {
socklen_t errlen = sizeof(err);
getsockopt(sockfd, SOL_SOCKET, SO_ERROR,
(void *)&err, &errlen);
}
#else
err = -errno;
if (err == -ENOTCONN) {
int ch;
if (read(sockfd, &ch, 1) < 0)
err = -errno;
/* It should *always* fail! */
129
}
130
131
#endif
return err;
132
133
}
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
/* checks whether the provided string is an IP or a hostname.
*/
unsigned string_is_hostname(const char *str)
{
struct in_addr buf;
/* We don't use inet_pton() because an IPv6 literal is likely to
be encased in []. So just check for a colon, which shouldn't
occur in hostnames anyway. */
if (!str || inet_aton(str, &buf) || strchr(str, ':'))
return 0;
return 1;
}
149
150
151
152
153
154
155
156
157
158
159
static int match_sockaddr(struct sockaddr *a, struct sockaddr *b)
{
if (a->sa_family == AF_INET) {
struct sockaddr_in *a4 = (void *)a;
struct sockaddr_in *b4 = (void *)b;
return (a4->sin_addr.s_addr == b4->sin_addr.s_addr) &&
(a4->sin_port == b4->sin_port);
} else if (a->sa_family == AF_INET6) {
struct sockaddr_in6 *a6 = (void *)a;
struct sockaddr_in6 *b6 = (void *)b;
160
161
return !memcmp(&a6->sin6_addr, &b6->sin6_addr, sizeof(a6->sin6_addr)) &&
a6->sin6_port == b6->sin6_port;
162
163
164
165
} else
return 0;
}
166
int connect_https_socket(struct openconnect_info *vpninfo)
167
{
168
int ssl_sock = -1;
169
170
int err;
171
172
173
if (!vpninfo->port)
vpninfo->port = 443;
174
175
176
177
178
/* If we're talking to a server which told us it has dynamic DNS, don't
just re-use its previous IP address. If we're talking to a proxy, we
can use *its* previous IP address. We expect it'll re-do the DNS
lookup for the server anyway. */
if (vpninfo->peer_addr && (!vpninfo->is_dyndns || vpninfo->proxy)) {
179
reconnect:
180
181
182
183
184
185
#ifdef SOCK_CLOEXEC
ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
if (ssl_sock < 0)
#endif
{
ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
186
187
188
189
190
191
if (ssl_sock < 0) {
#ifdef _WIN32
err = WSAGetLastError();
#else
err = -errno;
#endif
192
goto reconn_err;
193
}
194
set_fd_cloexec(ssl_sock);
195
}
196
197
198
err = cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen);
if (err) {
char *errstr;
199
reconn_err:
200
201
202
203
204
205
#ifdef _WIN32
if (err > 0)
errstr = openconnect__win32_strerror(err);
else
#endif
errstr = strerror(-err);
206
if (vpninfo->proxy) {
207
vpn_progress(vpninfo, PRG_ERR,
208
_("Failed to reconnect to proxy %s: %s\n"),
209
vpninfo->proxy, errstr);
210
} else {
211
vpn_progress(vpninfo, PRG_ERR,
212
_("Failed to reconnect to host %s: %s\n"),
213
vpninfo->hostname, errstr);
214
}
215
216
217
218
#ifdef _WIN32
if (err > 0)
free(errstr);
#endif
219
if (ssl_sock >= 0)
220
closesocket(ssl_sock);
221
222
ssl_sock = -EINVAL;
goto out;
223
}
224
225
} else {
struct addrinfo hints, *result, *rp;
226
char *hostname;
227
char port[6];
228
229
230
231
232
233
234
235
236
237
memset(&hints, 0, sizeof(struct addrinfo));
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
hints.ai_protocol = 0;
hints.ai_canonname = NULL;
hints.ai_addr = NULL;
hints.ai_next = NULL;
238
239
240
241
/* The 'port' variable is a string because it's easier
this way than if we pass NULL to getaddrinfo() and
then try to fill in the numeric value into
different types of returned sockaddr_in{6,}. */
242
#ifdef LIBPROXY_HDR
243
if (vpninfo->proxy_factory) {
244
struct oc_text_buf *url_buf = buf_alloc();
245
246
247
char **proxies;
int i = 0;
248
249
free(vpninfo->proxy_type);
vpninfo->proxy_type = NULL;
250
251
252
free(vpninfo->proxy);
vpninfo->proxy = NULL;
253
254
255
256
257
258
buf_append(url_buf, "https://%s", vpninfo->hostname);
if (vpninfo->port != 443)
buf_append(url_buf, ":%d", vpninfo->port);
buf_append(url_buf, "/%s", vpninfo->urlpath?:"");
if (buf_error(url_buf)) {
buf_free(url_buf);
259
260
261
ssl_sock = -ENOMEM;
goto out;
}
262
263
proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
264
url_buf->data);
265
i = 0;
266
while (proxies && proxies[i]) {
267
268
269
270
if (!vpninfo->proxy &&
(!strncmp(proxies[i], "http://", 7) ||
!strncmp(proxies[i], "socks://", 8) ||
!strncmp(proxies[i], "socks5://", 9)))
271
internal_parse_url(proxies[i], &vpninfo->proxy_type,
272
273
&vpninfo->proxy, &vpninfo->proxy_port,
NULL, 0);
274
275
i++;
}
276
buf_free(url_buf);
277
278
free(proxies);
if (vpninfo->proxy)
279
vpn_progress(vpninfo, PRG_DEBUG,
280
281
_("Proxy from libproxy: %s://%s:%d/\n"),
vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
282
283
}
#endif
284
285
if (vpninfo->proxy) {
hostname = vpninfo->proxy;
286
snprintf(port, 6, "%d", vpninfo->proxy_port);
287
288
} else {
hostname = vpninfo->hostname;
289
snprintf(port, 6, "%d", vpninfo->port);
290
291
}
292
if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
293
hostname = strndup(hostname + 1, strlen(hostname) - 2);
294
295
296
297
if (!hostname) {
ssl_sock = -ENOMEM;
goto out;
}
298
299
300
hints.ai_flags |= AI_NUMERICHOST;
}
301
302
303
304
if (vpninfo->getaddrinfo_override)
err = vpninfo->getaddrinfo_override(vpninfo->cbdata, hostname, port, &hints, &result);
else
err = getaddrinfo(hostname, port, &hints, &result);
305
306
if (err) {
307
308
309
vpn_progress(vpninfo, PRG_ERR,
_("getaddrinfo failed for host '%s': %s\n"),
hostname, gai_strerror(err));
310
311
if (hints.ai_flags & AI_NUMERICHOST)
free(hostname);
312
ssl_sock = -EINVAL;
313
314
315
316
317
318
319
/* If we were just retrying for dynamic DNS, reconnct using
the previously-known IP address */
if (vpninfo->peer_addr) {
vpn_progress(vpninfo, PRG_ERR,
_("Reconnecting to DynDNS server using previously cached IP address\n"));
goto reconnect;
}
320
goto out;
321
}
322
323
if (hints.ai_flags & AI_NUMERICHOST)
free(hostname);
324
325
for (rp = result; rp ; rp = rp->ai_next) {
326
327
char host[80];
328
host[0] = 0;
329
330
if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
sizeof(host), NULL, 0, NI_NUMERICHOST))
331
vpn_progress(vpninfo, PRG_DEBUG, vpninfo->proxy_type ?
332
_("Attempting to connect to proxy %s%s%s:%s\n") :
333
_("Attempting to connect to server %s%s%s:%s\n"),
334
rp->ai_family == AF_INET6 ? "[" : "",
335
host,
336
rp->ai_family == AF_INET6 ? "]" : "",
337
port);
338
339
340
341
342
ssl_sock = socket(rp->ai_family, rp->ai_socktype,
rp->ai_protocol);
if (ssl_sock < 0)
continue;
343
set_fd_cloexec(ssl_sock);
344
345
err = cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen);
if (!err) {
346
347
/* Store the peer address we actually used, so that DTLS can
use it again later */
348
349
350
351
352
free(vpninfo->ip_info.gateway_addr);
vpninfo->ip_info.gateway_addr = NULL;
if (host[0]) {
vpninfo->ip_info.gateway_addr = strdup(host);
353
354
355
356
357
vpn_progress(vpninfo, PRG_INFO, _("Connected to %s%s%s:%s\n"),
rp->ai_family == AF_INET6 ? "[" : "",
host,
rp->ai_family == AF_INET6 ? "]" : "",
port);
358
}
359
360
361
free(vpninfo->peer_addr);
vpninfo->peer_addrlen = 0;
362
363
vpninfo->peer_addr = malloc(rp->ai_addrlen);
if (!vpninfo->peer_addr) {
364
365
vpn_progress(vpninfo, PRG_ERR,
_("Failed to allocate sockaddr storage\n"));
366
closesocket(ssl_sock);
367
368
ssl_sock = -ENOMEM;
goto out;
369
370
371
}
vpninfo->peer_addrlen = rp->ai_addrlen;
memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
372
373
374
375
376
377
378
379
380
381
382
/* If no proxy, ensure that we output *this* IP address in
* authentication results because we're going to need to
* reconnect to the *same* server from the rotation. And with
* some trick DNS setups, it might possibly be a "rotation"
* even if we only got one result from getaddrinfo() this
* time.
*
* If there's a proxy, we're kind of screwed; we can't know
* which IP address we connected to. Perhaps we ought to do
* the DNS lookup locally and connect to a specific IP? */
if (!vpninfo->proxy && host[0]) {
383
char *p = malloc(strlen(host) + 3);
384
if (p) {
385
386
free(vpninfo->unique_hostname);
vpninfo->unique_hostname = p;
387
388
389
390
391
392
if (rp->ai_family == AF_INET6)
*p++ = '[';
memcpy(p, host, strlen(host));
p += strlen(host);
if (rp->ai_family == AF_INET6)
*p++ = ']';
393
*p = 0;
394
395
}
}
396
397
break;
}
398
399
400
401
402
403
404
405
406
if (host[0]) {
char *errstr;
#ifdef _WIN32
if (err > 0)
errstr = openconnect__win32_strerror(err);
else
#endif
errstr = strerror(-err);
407
408
409
410
vpn_progress(vpninfo, PRG_INFO, _("Failed to connect to %s%s%s:%s: %s\n"),
rp->ai_family == AF_INET6 ? "[" : "",
host,
rp->ai_family == AF_INET6 ? "]" : "",
411
412
413
414
415
416
port, errstr);
#ifdef _WIN32
if (err > 0)
free(errstr);
#endif
}
417
closesocket(ssl_sock);
418
ssl_sock = -1;
419
420
421
422
423
424
425
426
427
428
/* If we're in DynDNS mode but this *was* the cached IP address,
* don't bother falling back to it if it didn't work. */
if (vpninfo->peer_addr && vpninfo->peer_addrlen == rp->ai_addrlen &&
match_sockaddr(vpninfo->peer_addr, rp->ai_addr)) {
vpn_progress(vpninfo, PRG_TRACE,
_("Forgetting non-functional previous peer address\n"));
free(vpninfo->peer_addr);
vpninfo->peer_addr = 0;
vpninfo->peer_addrlen = 0;
429
430
free(vpninfo->ip_info.gateway_addr);
vpninfo->ip_info.gateway_addr = NULL;
431
}
432
433
}
freeaddrinfo(result);
434
435
if (ssl_sock < 0) {
436
437
438
vpn_progress(vpninfo, PRG_ERR,
_("Failed to connect to host %s\n"),
vpninfo->proxy?:vpninfo->hostname);
439
ssl_sock = -EINVAL;
440
441
442
443
444
if (vpninfo->peer_addr) {
vpn_progress(vpninfo, PRG_ERR,
_("Reconnecting to DynDNS server using previously cached IP address\n"));
goto reconnect;
}
445
goto out;
446
}
447
448
}
449
if (vpninfo->proxy) {
450
err = process_proxy(vpninfo, ssl_sock);
451
if (err) {
452
closesocket(ssl_sock);
453
454
455
456
457
458
if (err == -EAGAIN) {
/* Proxy authentication failed and we need to retry */
vpn_progress(vpninfo, PRG_DEBUG,
_("Reconnecting to proxy %s\n"), vpninfo->proxy);
goto reconnect;
}
459
ssl_sock = err;
460
461
}
}
462
463
464
out:
/* If proxy processing returned -EAGAIN to reconnect before attempting
further auth, and we failed to reconnect, we have to clean up here. */
465
clear_auth_states(vpninfo, vpninfo->proxy_auth, 1);
466
467
468
return ssl_sock;
}
469
470
471
472
473
474
475
476
477
478
479
int __attribute__ ((format (printf, 2, 3)))
openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...)
{
char buf[1024];
va_list args;
buf[1023] = 0;
va_start(args, fmt);
vsnprintf(buf, 1023, fmt, args);
va_end(args);
480
return vpninfo->ssl_write(vpninfo, buf, strlen(buf));
481
482
483
}
484
485
int __attribute__ ((format(printf, 4, 5)))
request_passphrase(struct openconnect_info *vpninfo, const char *label,
486
487
488
489
490
491
492
493
494
495
496
497
498
499
char **response, const char *fmt, ...)
{
struct oc_auth_form f;
struct oc_form_opt o;
char buf[1024];
va_list args;
int ret;
buf[1023] = 0;
memset(&f, 0, sizeof(f));
va_start(args, fmt);
vsnprintf(buf, 1023, fmt, args);
va_end(args);
500
f.auth_id = (char *)label;
501
502
503
504
f.opts = &o;
o.next = NULL;
o.type = OC_FORM_OPT_PASSWORD;
505
o.name = (char *)label;
506
o.label = buf;
507
o._value = NULL;
508
509
ret = process_auth_form(vpninfo, &f);
510
if (!ret) {
511
*response = o._value;
512
513
514
515
516
517
return 0;
}
return -EIO;
}
518
#if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
519
int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
520
521
{
struct statvfs buf;
522
523
char *sslkey = openconnect_utf8_to_legacy(vpninfo, vpninfo->sslkey);
int err = 0;
524
525
526
if (statvfs(sslkey, &buf)) {
err = -errno;
527
528
vpn_progress(vpninfo, PRG_ERR, _("statvfs: %s\n"),
strerror(errno));
529
} else if (asprintf(&vpninfo->cert_password, "%lx", buf.f_fsid) == -1)
530
531
532
533
534
err = -ENOMEM;
if (sslkey != vpninfo->sslkey)
free(sslkey);
return err;
535
}
536
537
#elif defined(_WIN32)
#include <fileapi.h>
538
539
typedef BOOL WINAPI (*GVIBH)(HANDLE, LPWSTR, DWORD, LPDWORD, LPDWORD, LPDWORD, LPWSTR, DWORD);
540
541
int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
{
542
HANDLE h;
543
DWORD serial;
544
545
HINSTANCE kernlib;
GVIBH func = NULL;
546
int success;
547
int fd;
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
/* Some versions of Windows don't have this so don't use standard
load-time linking or it'll cause failures. */
kernlib = LoadLibraryA("Kernel32.dll");
if (!kernlib) {
notsupp:
vpn_progress(vpninfo, PRG_ERR,
_("Could not obtain file system ID for passphrase\n"));
return -EOPNOTSUPP;
}
func = (GVIBH)GetProcAddress(kernlib, "GetVolumeInformationByHandleW");
FreeLibrary(kernlib);
if (!func)
goto notsupp;
563
fd = openconnect_open_utf8(vpninfo, vpninfo->sslkey, O_RDONLY);
564
565
566
567
568
569
if (fd == -1) {
vpn_progress(vpninfo, PRG_ERR,
_("Failed to open private key file '%s': %s\n"),
vpninfo->sslkey, strerror(errno));
return -ENOENT;
}
570
571
h = (HANDLE)_get_osfhandle(fd);
572
success = func(h, NULL, 0, &serial, NULL, NULL, NULL, 0);
573
close(fd);
574
575
if (!success)
576
577
return -EIO;
578
if (asprintf(&vpninfo->cert_password, "%lx", serial) == -1)
579
580
return -ENOMEM;
581
return 0;
582
}
583
#elif defined(HAVE_STATFS)
584
int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
585
{
586
char *sslkey = openconnect_utf8_to_legacy(vpninfo, vpninfo->sslkey);
587
588
589
struct statfs buf;
unsigned *fsid = (unsigned *)&buf.f_fsid;
unsigned long long fsid64;
590
int err = 0;
591
592
593
if (statfs(sslkey, &buf)) {
err = -errno;
594
595
vpn_progress(vpninfo, PRG_ERR, _("statfs: %s\n"),
strerror(errno));
596
return -err;
597
598
599
} else {
fsid64 = ((unsigned long long)fsid[0] << 32) | fsid[1];
600
if (asprintf(&vpninfo->cert_password, "%llx", fsid64) == -1)
601
err = -ENOMEM;
602
}
603
604
605
606
607
if (sslkey != vpninfo->sslkey)
free(sslkey);
return err;
608
}
609
610
611
612
613
#else
int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
{
return -EOPNOTSUPP;
}
614
#endif
615
616
#if defined(OPENCONNECT_OPENSSL)
617
618
/* We put this here rather than in openssl.c because it might be needed
for OpenSSL DTLS support even when GnuTLS is being used for HTTPS */
619
int openconnect_print_err_cb(const char *str, size_t len, void *ptr)
620
621
622
623
624
625
626
{
struct openconnect_info *vpninfo = ptr;
vpn_progress(vpninfo, PRG_ERR, "%s", str);
return 0;
}
#endif
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
#ifdef FAKE_ANDROID_KEYSTORE
char *keystore_strerror(int err)
{
return (char *)strerror(-err);
}
int keystore_fetch(const char *key, unsigned char **result)
{
unsigned char *data;
struct stat st;
int fd;
int ret;
fd = open(key, O_RDONLY);
if (fd < 0)
return -errno;
if (fstat(fd, &st)) {
ret = -errno;
goto out_fd;
}
650
data = malloc(st.st_size + 1);
651
652
653
654
655
656
657
658
659
660
if (!data) {
ret = -ENOMEM;
goto out_fd;
}
if (read(fd, data, st.st_size) != st.st_size) {
ret = -EIO;
free(data);
goto out_fd;
}
661
662
data[st.st_size] = 0;
663
664
665
666
667
668
*result = data;
ret = st.st_size;
out_fd:
close(fd);
return ret;
}
669
#elif defined(ANDROID_KEYSTORE)
670
671
672
673
674
675
676
677
678
679
680
681
682
/* keystore.h isn't in the NDK so we need to define these */
#define NO_ERROR 1
#define LOCKED 2
#define UNINITIALIZED 3
#define SYSTEM_ERROR 4
#define PROTOCOL_ERROR 5
#define PERMISSION_DENIED 6
#define KEY_NOT_FOUND 7
#define VALUE_CORRUPTED 8
#define UNDEFINED_ACTION 9
#define WRONG_PASSWORD 10
const char *keystore_strerror(int err)
683
684
685
{
switch (-err) {
case NO_ERROR: return _("No error");
686
case LOCKED: return _("Keystore locked");
687
688
689
690
691
692
693
case UNINITIALIZED: return _("Keystore uninitialized");
case SYSTEM_ERROR: return _("System error");
case PROTOCOL_ERROR: return _("Protocol error");
case PERMISSION_DENIED: return _("Permission denied");
case KEY_NOT_FOUND: return _("Key not found");
case VALUE_CORRUPTED: return _("Value corrupted");
case UNDEFINED_ACTION: return _("Undefined action");
694
695
696
697
case WRONG_PASSWORD:
case WRONG_PASSWORD+1:
case WRONG_PASSWORD+2:
case WRONG_PASSWORD+3: return _("Wrong password");
698
699
700
701
702
703
704
705
default: return _("Unknown error");
}
}
/* Returns length, or a negative errno in its own namespace (handled by its
own strerror function above). The numbers are from Android's keystore.h */
int keystore_fetch(const char *key, unsigned char **result)
{
706
707
struct sockaddr_un sa = { AF_UNIX, "/dev/socket/keystore" };
socklen_t sl = offsetof(struct sockaddr_un, sun_path) + strlen(sa.sun_path) + 1;
708
709
unsigned char *data, *p;
unsigned char buf[3];
710
int len, fd;
711
712
int ret = -SYSTEM_ERROR;
713
fd = socket(AF_UNIX, SOCK_STREAM, 0);
714
715
716
if (fd < 0)
return -SYSTEM_ERROR;
717
718
719
720
if (connect(fd, (void *)&sa, sl)) {
close(fd);
return -SYSTEM_ERROR;
}
721
722
len = strlen(key);
buf[0] = 'g';
723
store_be16(buf + 1, len);
724
725
726
727
728
729
730
731
732
733
734
735
if (send(fd, buf, 3, 0) != 3 || send(fd, key, len, 0) != len ||
shutdown(fd, SHUT_WR) || recv(fd, buf, 1, 0) != 1)
goto out;
if (buf[0] != NO_ERROR) {
/* Should never be zero */
ret = buf[0] ? -buf[0] : -PROTOCOL_ERROR;
goto out;
}
if (recv(fd, buf, 2, 0) != 2)
goto out;
736
len = load_be16(buf);
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
data = malloc(len);
if (!data)
goto out;
p = data;
ret = len;
while (len) {
int got = recv(fd, p, len, 0);
if (got <= 0) {
free(data);
ret = -PROTOCOL_ERROR;
goto out;
}
len -= got;
p += got;
}
*result = data;
out:
close(fd);
return ret;
}
#endif
760
761
762
void cmd_fd_set(struct openconnect_info *vpninfo, fd_set *fds, int *maxfd)
{
763
764
765
766
if (vpninfo->cmd_fd != -1) {
FD_SET(vpninfo->cmd_fd, fds);
if (vpninfo->cmd_fd > *maxfd)
*maxfd = vpninfo->cmd_fd;
767
768
769
}
}
770
771
772
773
774
775
776
777
778
779
780
781
void check_cmd_fd(struct openconnect_info *vpninfo, fd_set *fds)
{
char cmd;
if (vpninfo->cmd_fd == -1 || !FD_ISSET(vpninfo->cmd_fd, fds))
return;
if (vpninfo->cmd_fd_write == -1) {
/* legacy openconnect_set_cancel_fd() users */
vpninfo->got_cancel_cmd = 1;
return;
}
782
#ifdef _WIN32
783
if (recv(vpninfo->cmd_fd, &cmd, 1, 0) != 1)
784
return;
785
786
787
788
#else
if (read(vpninfo->cmd_fd, &cmd, 1) != 1)
return;
#endif
789
790
switch (cmd) {
case OC_CMD_CANCEL:
791
case OC_CMD_DETACH:
792
vpninfo->got_cancel_cmd = 1;
793
vpninfo->cancel_type = cmd;
794
break;
795
796
797
case OC_CMD_PAUSE:
vpninfo->got_pause_cmd = 1;
break;
798
799
800
case OC_CMD_STATS:
if (vpninfo->stats_handler)
vpninfo->stats_handler(vpninfo->cbdata, &vpninfo->stats);
801
802
803
}
}
804
805
int is_cancel_pending(struct openconnect_info *vpninfo, fd_set *fds)
{
806
check_cmd_fd(vpninfo, fds);
807
return vpninfo->got_cancel_cmd || vpninfo->got_pause_cmd;
808
809
810
811
812
813
}
void poll_cmd_fd(struct openconnect_info *vpninfo, int timeout)
{
fd_set rd_set;
int maxfd = 0;
814
time_t expiration = time(NULL) + timeout, now = 0;
815
816
while (now < expiration && !vpninfo->got_cancel_cmd && !vpninfo->got_pause_cmd) {
817
struct timeval tv;
818
819
820
now = time(NULL);
tv.tv_sec = now >= expiration ? 0 : expiration - now;
821
tv.tv_usec = 0;
822
823
824
825
826
FD_ZERO(&rd_set);
cmd_fd_set(vpninfo, &rd_set, &maxfd);
select(maxfd + 1, &rd_set, NULL, NULL, &tv);
check_cmd_fd(vpninfo, &rd_set);
827
}
828
}
829
830
#ifdef _WIN32
831
832
#include <io.h>
#include <sys/stat.h>
833
int openconnect_open_utf8(struct openconnect_info *vpninfo, const char *fname, int mode)
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
{
wchar_t *fname_w;
int nr_chars = MultiByteToWideChar(CP_UTF8, 0, fname, -1, NULL, 0);
int fd;
if (!nr_chars) {
errno = EINVAL;
return -1;
}
fname_w = malloc(nr_chars * sizeof(wchar_t));
if (!fname_w) {
errno = ENOMEM;
return -1;
}
MultiByteToWideChar(CP_UTF8, 0, fname, -1, fname_w, nr_chars);
850
fd = _wopen(fname_w, mode, _S_IREAD | _S_IWRITE);
851
852
853
854
855
free(fname_w);
return fd;
}
#else
856
int openconnect_open_utf8(struct openconnect_info *vpninfo, const char *fname, int mode)
857
858
859
860
{
char *legacy_fname = openconnect_utf8_to_legacy(vpninfo, fname);
int fd;
861
fd = open(legacy_fname, mode, 0644);
862
863
864
865
866
867
868
if (legacy_fname != fname)
free(legacy_fname);
return fd;
}
#endif
869
870
FILE *openconnect_fopen_utf8(struct openconnect_info *vpninfo, const char *fname,
const char *mode)
871
872
{
int fd;
873
874
875
876
877
878
879
880
881
882
883
884
885
int flags;
if (!strcmp(mode, "r"))
flags = O_RDONLY|O_CLOEXEC;
else if (!strcmp(mode, "rb"))
flags = O_RDONLY|O_CLOEXEC|O_BINARY;
else if (!strcmp(mode, "w"))
flags = O_WRONLY|O_CLOEXEC|O_CREAT|O_TRUNC;
else if (!strcmp(mode, "wb"))
flags = O_WRONLY|O_CLOEXEC|O_CREAT|O_TRUNC|O_BINARY;
else {
/* This should never happen, but if we forget and start using other
modes without implementing proper mode->flags conversion, complain! */
886
vpn_progress(vpninfo, PRG_ERR,
887
_("openconnect_fopen_utf8() used with unsupported mode '%s'\n"),
888
889
890
891
mode);
return NULL;
}
892
fd = openconnect_open_utf8(vpninfo, fname, flags);
893
894
895
896
897
if (fd == -1)
return NULL;
return fdopen(fd, mode);
}
898
899
900
int udp_sockaddr(struct openconnect_info *vpninfo, int port)
{
901
free(vpninfo->dtls_addr);
902
903
904
905
906
907
908
909
910
vpninfo->dtls_addr = malloc(vpninfo->peer_addrlen);
if (!vpninfo->dtls_addr)
return -ENOMEM;
memcpy(vpninfo->dtls_addr, vpninfo->peer_addr, vpninfo->peer_addrlen);
if (vpninfo->peer_addr->sa_family == AF_INET) {
struct sockaddr_in *sin = (void *)vpninfo->dtls_addr;
sin->sin_port = htons(port);
911
912
vpninfo->dtls_tos_proto = IPPROTO_IP;
vpninfo->dtls_tos_optname = IP_TOS;
913
914
915
} else if (vpninfo->peer_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *sin = (void *)vpninfo->dtls_addr;
sin->sin6_port = htons(port);
916
917
vpninfo->dtls_tos_proto = IPPROTO_IPV6;
vpninfo->dtls_tos_optname = IPV6_TCLASS;
918
919
920
921
922
923
924
} else {
vpn_progress(vpninfo, PRG_ERR,
_("Unknown protocol family %d. Cannot create UDP server address\n"),
vpninfo->peer_addr->sa_family);
return -EINVAL;
}
925
926
927
928
929
/* in case DTLS TOS copy is disabled, reset the optname value */
/* so that the copy won't be applied in dtls.c / dtls_mainloop() */
if (!vpninfo->dtls_pass_tos)
vpninfo->dtls_tos_optname = 0;
930
931
return 0;
}
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
int udp_connect(struct openconnect_info *vpninfo)
{
int fd, sndbuf;
fd = socket(vpninfo->peer_addr->sa_family, SOCK_DGRAM, IPPROTO_UDP);
if (fd < 0) {
vpn_perror(vpninfo, _("Open UDP socket"));
return -EINVAL;
}
if (vpninfo->protect_socket)
vpninfo->protect_socket(vpninfo->cbdata, fd);
sndbuf = vpninfo->ip_info.mtu * 2;
setsockopt(fd, SOL_SOCKET, SO_SNDBUF, (void *)&sndbuf, sizeof(sndbuf));
if (vpninfo->dtls_local_port) {
union {
struct sockaddr_in in;
struct sockaddr_in6 in6;
} dtls_bind_addr;
int dtls_bind_addrlen;
memset(&dtls_bind_addr, 0, sizeof(dtls_bind_addr));
if (vpninfo->peer_addr->sa_family == AF_INET) {
struct sockaddr_in *addr = &dtls_bind_addr.in;
dtls_bind_addrlen = sizeof(*addr);
addr->sin_family = AF_INET;
addr->sin_addr.s_addr = INADDR_ANY;
addr->sin_port = htons(vpninfo->dtls_local_port);
} else if (vpninfo->peer_addr->sa_family == AF_INET6) {
struct sockaddr_in6 *addr = &dtls_bind_addr.in6;
dtls_bind_addrlen = sizeof(*addr);
addr->sin6_family = AF_INET6;
addr->sin6_addr = in6addr_any;
addr->sin6_port = htons(vpninfo->dtls_local_port);
} else {
vpn_progress(vpninfo, PRG_ERR,
_("Unknown protocol family %d. Cannot use UDP transport\n"),
vpninfo->peer_addr->sa_family);
vpninfo->dtls_attempt_period = 0;
closesocket(fd);
return -EINVAL;
}
if (bind(fd, (struct sockaddr *)&dtls_bind_addr, dtls_bind_addrlen)) {
vpn_perror(vpninfo, _("Bind UDP socket"));
closesocket(fd);
return -EINVAL;
}
}
if (connect(fd, vpninfo->dtls_addr, vpninfo->peer_addrlen)) {
vpn_perror(vpninfo, _("Connect UDP socket\n"));
closesocket(fd);
return -EINVAL;
}
set_fd_cloexec(fd);
set_sock_nonblock(fd);
return fd;
}
995
996
997
998
999
1000
int ssl_reconnect(struct openconnect_info *vpninfo)
{
int ret;
int timeout;
int interval;