2 * OpenConnect (SSL + DTLS) VPN client
4 * Copyright © 2008-2012 Intel Corporation.
6 * Author: David Woodhouse <dwmw2@infradead.org>
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.
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.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with this library; if not, write to:
20 * Free Software Foundation, Inc.
21 * 51 Franklin Street, Fifth Floor,
22 * Boston, MA 02110-1301 USA
25 #include <sys/types.h>
26 #include <sys/socket.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
38 #if defined(__linux__) || defined(ANDROID)
40 #elif defined(__FreeBSD__) || defined(__FreeBSD_kernel__) || defined(__OpenBSD__) || defined(__APPLE__)
41 #include <sys/param.h>
42 #include <sys/mount.h>
43 #elif defined (__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
44 #include <sys/statvfs.h>
45 #elif defined (__GNU__)
46 #include <sys/statfs.h>
49 #include "openconnect-internal.h"
51 /* OSX < 1.6 doesn't have AI_NUMERICSERV */
52 #ifndef AI_NUMERICSERV
53 #define AI_NUMERICSERV 0
56 static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
57 const struct sockaddr *addr, socklen_t addrlen)
59 struct sockaddr_storage peer;
60 socklen_t peerlen = sizeof(peer);
61 fd_set wr_set, rd_set;
64 fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
66 if (connect(sockfd, addr, addrlen) < 0 && errno != EINPROGRESS)
71 FD_SET(sockfd, &wr_set);
72 if (vpninfo->cancel_fd != -1) {
73 FD_SET(vpninfo->cancel_fd, &rd_set);
74 if (vpninfo->cancel_fd > sockfd)
75 maxfd = vpninfo->cancel_fd;
78 /* Later we'll render this whole exercise non-pointless by
79 including a 'cancelfd' here too. */
80 select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
81 if (vpninfo->cancel_fd != -1 && FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
82 vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n"));
87 /* Check whether connect() succeeded or failed by using
88 getpeername(). See http://cr.yp.to/docs/connect.html */
89 return getpeername(sockfd, (void *)&peer, &peerlen);
92 int connect_https_socket(struct openconnect_info *vpninfo)
100 if (vpninfo->peer_addr) {
102 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
106 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
109 fcntl(ssl_sock, F_SETFD, fcntl(ssl_sock, F_GETFD) | FD_CLOEXEC);
111 if (cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen)) {
113 if (vpninfo->proxy) {
114 vpn_progress(vpninfo, PRG_ERR,
115 _("Failed to reconnect to proxy %s\n"),
118 vpn_progress(vpninfo, PRG_ERR,
119 _("Failed to reconnect to host %s\n"),
127 struct addrinfo hints, *result, *rp;
131 memset(&hints, 0, sizeof(struct addrinfo));
132 hints.ai_family = AF_UNSPEC;
133 hints.ai_socktype = SOCK_STREAM;
134 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
135 hints.ai_protocol = 0;
136 hints.ai_canonname = NULL;
137 hints.ai_addr = NULL;
138 hints.ai_next = NULL;
140 /* The 'port' variable is a string because it's easier
141 this way than if we pass NULL to getaddrinfo() and
142 then try to fill in the numeric value into
143 different types of returned sockaddr_in{6,}. */
145 if (vpninfo->proxy_factory) {
150 free(vpninfo->proxy_type);
151 vpninfo->proxy_type = NULL;
152 free(vpninfo->proxy);
153 vpninfo->proxy = NULL;
155 if (vpninfo->port == 443)
156 i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
157 vpninfo->urlpath?:"");
159 i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
160 vpninfo->port, vpninfo->urlpath?:"");
164 proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
168 while (proxies && proxies[i]) {
169 if (!vpninfo->proxy &&
170 (!strncmp(proxies[i], "http://", 7) ||
171 !strncmp(proxies[i], "socks://", 8) ||
172 !strncmp(proxies[i], "socks5://", 9)))
173 internal_parse_url(proxies[i], &vpninfo->proxy_type,
174 &vpninfo->proxy, &vpninfo->proxy_port,
181 vpn_progress(vpninfo, PRG_TRACE,
182 _("Proxy from libproxy: %s://%s:%d/\n"),
183 vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
186 if (vpninfo->proxy) {
187 hostname = vpninfo->proxy;
188 snprintf(port, 6, "%d", vpninfo->proxy_port);
190 hostname = vpninfo->hostname;
191 snprintf(port, 6, "%d", vpninfo->port);
194 if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
195 /* Solaris has no strndup(). */
196 int len = strlen(hostname) - 2;
197 char *new_hostname = malloc(len + 1);
200 memcpy(new_hostname, hostname + 1, len);
201 new_hostname[len] = 0;
203 hostname = new_hostname;
204 hints.ai_flags |= AI_NUMERICHOST;
207 err = getaddrinfo(hostname, port, &hints, &result);
210 vpn_progress(vpninfo, PRG_ERR,
211 _("getaddrinfo failed for host '%s': %s\n"),
212 hostname, gai_strerror(err));
213 if (hints.ai_flags & AI_NUMERICHOST)
217 if (hints.ai_flags & AI_NUMERICHOST)
220 for (rp = result; rp ; rp = rp->ai_next) {
224 if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
225 sizeof(host), NULL, 0, NI_NUMERICHOST))
226 vpn_progress(vpninfo, PRG_INFO, vpninfo->proxy_type?
227 _("Attempting to connect to proxy %s%s%s:%s\n"):
228 _("Attempting to connect to server %s%s%s:%s\n"),
229 rp->ai_family == AF_INET6?"[":"",
231 rp->ai_family == AF_INET6?"]":"",
234 ssl_sock = socket(rp->ai_family, rp->ai_socktype,
238 if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
239 /* Store the peer address we actually used, so that DTLS can
240 use it again later */
241 vpninfo->peer_addr = malloc(rp->ai_addrlen);
242 if (!vpninfo->peer_addr) {
243 vpn_progress(vpninfo, PRG_ERR,
244 _("Failed to allocate sockaddr storage\n"));
248 vpninfo->peer_addrlen = rp->ai_addrlen;
249 memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
250 /* If no proxy, and if more than one address for the hostname,
251 ensure that we output the same IP address in authentication
252 results (from libopenconnect or --authenticate). */
253 if (!vpninfo->proxy && (rp != result || rp->ai_next) && host[0]) {
254 char *p = malloc(strlen(host) + 3);
256 free(vpninfo->hostname);
257 vpninfo->hostname = p;
258 if (rp->ai_family == AF_INET6)
260 memcpy(p, host, strlen(host));
262 if (rp->ai_family == AF_INET6)
272 freeaddrinfo(result);
275 vpn_progress(vpninfo, PRG_ERR,
276 _("Failed to connect to host %s\n"),
277 vpninfo->proxy?:vpninfo->hostname);
282 if (vpninfo->proxy) {
283 err = process_proxy(vpninfo, ssl_sock);
293 int __attribute__ ((format (printf, 2, 3)))
294 openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...)
302 vsnprintf(buf, 1023, fmt, args);
304 return openconnect_SSL_write(vpninfo, buf, strlen(buf));
308 int request_passphrase(struct openconnect_info *vpninfo, const char *label,
309 char **response, const char *fmt, ...)
311 struct oc_auth_form f;
312 struct oc_form_opt o;
317 if (!vpninfo->process_auth_form)
321 memset(&f, 0, sizeof(f));
323 vsnprintf(buf, 1023, fmt, args);
326 f.auth_id = (char *)label;
330 o.type = OC_FORM_OPT_PASSWORD;
331 o.name = (char *)label;
335 ret = vpninfo->process_auth_form(vpninfo->cbdata, &f);
344 #if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
345 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
349 if (statvfs(vpninfo->sslkey, &buf)) {
351 vpn_progress(vpninfo, PRG_ERR, _("statvfs: %s\n"),
355 if (asprintf(&vpninfo->cert_password, "%lx", buf.f_fsid))
360 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
363 unsigned *fsid = (unsigned *)&buf.f_fsid;
364 unsigned long long fsid64;
366 if (statfs(vpninfo->sslkey, &buf)) {
368 vpn_progress(vpninfo, PRG_ERR, _("statfs: %s\n"),
372 fsid64 = ((unsigned long long)fsid[0] << 32) | fsid[1];
374 if (asprintf(&vpninfo->cert_password, "%llx", fsid64))
380 #if defined(OPENCONNECT_OPENSSL) || defined (DTLS_OPENSSL)
381 /* We put this here rather than in openssl.c because it might be needed
382 for OpenSSL DTLS support even when GnuTLS is being used for HTTPS */
383 int openconnect_print_err_cb(const char *str, size_t len, void *ptr)
385 struct openconnect_info *vpninfo = ptr;
387 vpn_progress(vpninfo, PRG_ERR, "%s", str);
392 #ifdef FAKE_ANDROID_KEYSTORE
393 char *keystore_strerror(int err)
395 return (char *)strerror(-err);
398 int keystore_fetch(const char *key, unsigned char **result)
405 fd = open(key, O_RDONLY);
409 if (fstat(fd, &st)) {
414 data = malloc(st.st_size + 1);
420 if (read(fd, data, st.st_size) != st.st_size) {
426 data[st.st_size] = 0;
433 #elif defined (ANDROID_KEYSTORE)
434 #include <cutils/sockets.h>
435 #include <keystore.h>
436 char *keystore_strerror(int err)
439 case NO_ERROR: return _("No error");
440 case LOCKED: return _("Keystore locked");
441 case UNINITIALIZED: return _("Keystore uninitialized");
442 case SYSTEM_ERROR: return _("System error");
443 case PROTOCOL_ERROR: return _("Protocol error");
444 case PERMISSION_DENIED: return _("Permission denied");
445 case KEY_NOT_FOUND: return _("Key not found");
446 case VALUE_CORRUPTED: return _("Value corrupted");
447 case UNDEFINED_ACTION: return _("Undefined action");
448 case WRONG_PASSWORD_0:
449 case WRONG_PASSWORD_1:
450 case WRONG_PASSWORD_2:
451 case WRONG_PASSWORD_3: return _("Wrong password");
452 default: return _("Unknown error");
456 /* Returns length, or a negative errno in its own namespace (handled by its
457 own strerror function above). The numbers are from Android's keystore.h */
458 int keystore_fetch(const char *key, unsigned char **result)
460 unsigned char *data, *p;
461 unsigned char buf[3];
463 int ret = -SYSTEM_ERROR;
465 fd = socket_local_client("keystore",
466 ANDROID_SOCKET_NAMESPACE_RESERVED,
469 return -SYSTEM_ERROR;
476 if (send(fd, buf, 3, 0) != 3 || send(fd, key, len, 0) != len ||
477 shutdown(fd, SHUT_WR) || recv(fd, buf, 1, 0) != 1)
480 if (buf[0] != NO_ERROR) {
481 /* Should never be zero */
482 ret = buf[0] ? -buf[0] : -PROTOCOL_ERROR;
485 if (recv(fd, buf, 2, 0) != 2)
487 len = (buf[0] << 8) + buf[1];
494 int got = recv(fd, p, len, 0);
497 ret = -PROTOCOL_ERROR;