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