Switch from Android's keystore_get() to our own keystore_fetch()
[platform/upstream/openconnect.git] / ssl.c
1 /*
2  * OpenConnect (SSL + DTLS) VPN client
3  *
4  * Copyright © 2008-2012 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  * You should have received a copy of the GNU Lesser General Public
18  * License along with this library; if not, write to:
19  *
20  *   Free Software Foundation, Inc.
21  *   51 Franklin Street, Fifth Floor,
22  *   Boston, MA 02110-1301 USA
23  */
24
25 #include <sys/types.h>
26 #include <sys/socket.h>
27 #include <sys/stat.h>
28 #include <netinet/in.h>
29 #include <arpa/inet.h>
30 #include <netdb.h>
31 #include <unistd.h>
32 #include <fcntl.h>
33 #include <string.h>
34 #include <stdio.h>
35 #include <errno.h>
36 #include <stdlib.h>
37 #include <stdarg.h>
38 #if defined(__linux__) || defined(ANDROID)
39 #include <sys/vfs.h>
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>
47 #endif
48
49 #include "openconnect-internal.h"
50
51 /* OSX < 1.6 doesn't have AI_NUMERICSERV */
52 #ifndef AI_NUMERICSERV
53 #define AI_NUMERICSERV 0
54 #endif
55
56 static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
57                                const struct sockaddr *addr, socklen_t addrlen)
58 {
59         struct sockaddr_storage peer;
60         socklen_t peerlen = sizeof(peer);
61         fd_set wr_set, rd_set;
62         int maxfd = sockfd;
63
64         fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
65
66         if (connect(sockfd, addr, addrlen) < 0 && errno != EINPROGRESS)
67                 return -1;
68
69         FD_ZERO(&wr_set);
70         FD_ZERO(&rd_set);
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;
76         }
77         
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"));
83                 errno = EINTR;
84                 return -1;
85         }
86                 
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);
90 }
91
92 int connect_https_socket(struct openconnect_info *vpninfo)
93 {
94         int ssl_sock = -1;
95         int err;
96
97         if (!vpninfo->port)
98                 vpninfo->port = 443;
99
100         if (vpninfo->peer_addr) {
101 #ifdef SOCK_CLOEXEC
102                 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
103                 if (ssl_sock < 0)
104 #endif
105                 {
106                         ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
107                         if (ssl_sock < 0)
108                                 goto reconn_err;
109                         fcntl(ssl_sock, F_SETFD, fcntl(ssl_sock, F_GETFD) | FD_CLOEXEC);
110                 }
111                 if (cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen)) {
112                 reconn_err:
113                         if (vpninfo->proxy) {
114                                 vpn_progress(vpninfo, PRG_ERR, 
115                                              _("Failed to reconnect to proxy %s\n"),
116                                              vpninfo->proxy);
117                         } else {
118                                 vpn_progress(vpninfo, PRG_ERR, 
119                                              _("Failed to reconnect to host %s\n"),
120                                              vpninfo->hostname);
121                         }
122                         return -EINVAL;
123                 }
124                 
125         } else {
126                 struct addrinfo hints, *result, *rp;
127                 char *hostname;
128                 char port[6];
129
130                 memset(&hints, 0, sizeof(struct addrinfo));
131                 hints.ai_family = AF_UNSPEC;
132                 hints.ai_socktype = SOCK_STREAM;
133                 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
134                 hints.ai_protocol = 0;
135                 hints.ai_canonname = NULL;
136                 hints.ai_addr = NULL;
137                 hints.ai_next = NULL;
138
139                 /* The 'port' variable is a string because it's easier
140                    this way than if we pass NULL to getaddrinfo() and
141                    then try to fill in the numeric value into
142                    different types of returned sockaddr_in{6,}. */
143 #ifdef LIBPROXY_HDR
144                 if (vpninfo->proxy_factory) {
145                         char *url;
146                         char **proxies;
147                         int i = 0;
148
149                         free(vpninfo->proxy_type);
150                         vpninfo->proxy_type = NULL;
151                         free(vpninfo->proxy);
152                         vpninfo->proxy = NULL;
153
154                         if (vpninfo->port == 443)
155                                 i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
156                                              vpninfo->urlpath?:"");
157                         else
158                                 i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
159                                              vpninfo->port, vpninfo->urlpath?:"");
160                         if (i == -1)
161                                 return -ENOMEM;
162
163                         proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
164                                                                url);
165
166                         i = 0;
167                         while (proxies && proxies[i]) {
168                                 if (!vpninfo->proxy &&
169                                     (!strncmp(proxies[i], "http://", 7) ||
170                                      !strncmp(proxies[i], "socks://", 8) ||
171                                      !strncmp(proxies[i], "socks5://", 9)))
172                                         internal_parse_url(proxies[i], &vpninfo->proxy_type,
173                                                   &vpninfo->proxy, &vpninfo->proxy_port,
174                                                   NULL, 0);
175                                 i++;
176                         }
177                         free(url);
178                         free(proxies);
179                         if (vpninfo->proxy)
180                                 vpn_progress(vpninfo, PRG_TRACE,
181                                              _("Proxy from libproxy: %s://%s:%d/\n"),
182                                              vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
183                 }
184 #endif
185                 if (vpninfo->proxy) {
186                         hostname = vpninfo->proxy;
187                         snprintf(port, 6, "%d", vpninfo->proxy_port);
188                 } else {
189                         hostname = vpninfo->hostname;
190                         snprintf(port, 6, "%d", vpninfo->port);
191                 }
192
193                 if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
194                         /* Solaris has no strndup(). */
195                         int len = strlen(hostname) - 2;
196                         char *new_hostname = malloc(len + 1);
197                         if (!new_hostname)
198                                 return -ENOMEM;
199                         memcpy(new_hostname, hostname + 1, len);
200                         new_hostname[len] = 0;
201
202                         hostname = new_hostname;
203                         hints.ai_flags |= AI_NUMERICHOST;
204                 }
205
206                 err = getaddrinfo(hostname, port, &hints, &result);
207                 if (hints.ai_flags & AI_NUMERICHOST)
208                         free(hostname);
209
210                 if (err) {
211                         vpn_progress(vpninfo, PRG_ERR,
212                                      _("getaddrinfo failed for host '%s': %s\n"),
213                                      hostname, gai_strerror(err));
214                         return -EINVAL;
215                 }
216
217                 for (rp = result; rp ; rp = rp->ai_next) {
218                         char host[80];
219
220                         if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
221                                          sizeof(host), NULL, 0, NI_NUMERICHOST))
222                                 vpn_progress(vpninfo, PRG_INFO,
223                                              _("Attempting to connect to %s%s%s:%s\n"),
224                                              rp->ai_family == AF_INET6?"[":"",
225                                              host,
226                                              rp->ai_family == AF_INET6?"]":"",
227                                              port);
228                         
229                         ssl_sock = socket(rp->ai_family, rp->ai_socktype,
230                                           rp->ai_protocol);
231                         if (ssl_sock < 0)
232                                 continue;
233                         if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
234                                 /* Store the peer address we actually used, so that DTLS can
235                                    use it again later */
236                                 vpninfo->peer_addr = malloc(rp->ai_addrlen);
237                                 if (!vpninfo->peer_addr) {
238                                         vpn_progress(vpninfo, PRG_ERR,
239                                                      _("Failed to allocate sockaddr storage\n"));
240                                         close(ssl_sock);
241                                         return -ENOMEM;
242                                 }
243                                 vpninfo->peer_addrlen = rp->ai_addrlen;
244                                 memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
245                                 break;
246                         }
247                         close(ssl_sock);
248                         ssl_sock = -1;
249                 }
250                 freeaddrinfo(result);
251                 
252                 if (ssl_sock < 0) {
253                         vpn_progress(vpninfo, PRG_ERR,
254                                      _("Failed to connect to host %s\n"),
255                                      vpninfo->proxy?:vpninfo->hostname);
256                         return -EINVAL;
257                 }
258         }
259
260         if (vpninfo->proxy) {
261                 err = process_proxy(vpninfo, ssl_sock);
262                 if (err) {
263                         close(ssl_sock);
264                         return err;
265                 }
266         }
267
268         return ssl_sock;
269 }
270
271 int  __attribute__ ((format (printf, 2, 3)))
272     openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...)
273 {
274         char buf[1024];
275         va_list args;
276
277         buf[1023] = 0;
278
279         va_start(args, fmt);
280         vsnprintf(buf, 1023, fmt, args);
281         va_end(args);
282         return openconnect_SSL_write(vpninfo, buf, strlen(buf));
283
284 }
285
286 int request_passphrase(struct openconnect_info *vpninfo, const char *label,
287                        char **response, const char *fmt, ...)
288 {
289         struct oc_auth_form f;
290         struct oc_form_opt o;
291         char buf[1024];
292         va_list args;
293         int ret;
294
295         if (!vpninfo->process_auth_form)
296                 return -EINVAL;
297
298         buf[1023] = 0;
299         memset(&f, 0, sizeof(f));
300         va_start(args, fmt);
301         vsnprintf(buf, 1023, fmt, args);
302         va_end(args);
303
304         f.auth_id = (char *)label;
305         f.opts = &o;
306
307         o.next = NULL;
308         o.type = OC_FORM_OPT_PASSWORD;
309         o.name = (char *)label;
310         o.label = buf;
311         o.value = NULL;
312
313         ret = vpninfo->process_auth_form(vpninfo->cbdata, &f);
314         if (!ret) {
315                 *response = o.value;
316                 return 0;
317         }
318
319         return -EIO;
320 }
321
322 #if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
323 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
324 {
325         struct statvfs buf;
326
327         if (statvfs(vpninfo->sslkey, &buf)) {
328                 int err = errno;
329                 vpn_progress(vpninfo, PRG_ERR, _("statvfs: %s\n"),
330                              strerror(errno));
331                 return -err;
332         }
333         if (asprintf(&vpninfo->cert_password, "%lx", buf.f_fsid))
334                 return -ENOMEM;
335         return 0;
336 }
337 #else
338 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
339 {
340         struct statfs buf;
341         unsigned *fsid = (unsigned *)&buf.f_fsid;
342         unsigned long long fsid64;
343
344         if (statfs(vpninfo->sslkey, &buf)) {
345                 int err = errno;
346                 vpn_progress(vpninfo, PRG_ERR, _("statfs: %s\n"),
347                              strerror(errno));
348                 return -err;
349         }
350         fsid64 = ((unsigned long long)fsid[0] << 32) | fsid[1];
351
352         if (asprintf(&vpninfo->cert_password, "%llx", fsid64))
353                 return -ENOMEM;
354         return 0;
355 }
356 #endif
357
358 #if defined(OPENCONNECT_OPENSSL) || defined (DTLS_OPENSSL)
359 /* We put this here rather than in openssl.c because it might be needed
360    for OpenSSL DTLS support even when GnuTLS is being used for HTTPS */
361 int openconnect_print_err_cb(const char *str, size_t len, void *ptr)
362 {
363         struct openconnect_info *vpninfo = ptr;
364
365         vpn_progress(vpninfo, PRG_ERR, "%s", str);
366         return 0;
367 }
368 #endif
369
370 #ifdef FAKE_ANDROID_KEYSTORE
371 char *keystore_strerror(int err)
372 {
373         return (char *)strerror(-err);
374 }
375
376 int keystore_fetch(const char *key, unsigned char **result)
377 {
378         unsigned char *data;
379         struct stat st;
380         int fd;
381         int ret;
382
383         fd = open(key, O_RDONLY);
384         if (fd < 0)
385                 return -errno;
386
387         if (fstat(fd, &st)) {
388                 ret = -errno;
389                 goto out_fd;
390         }
391
392         data = malloc(st.st_size);
393         if (!data) {
394                 ret = -ENOMEM;
395                 goto out_fd;
396         }
397
398         if (read(fd, data, st.st_size) != st.st_size) {
399                 ret = -EIO;
400                 free(data);
401                 goto out_fd;
402         }
403         *result = data;
404         ret = st.st_size;
405  out_fd:
406         close(fd);
407         return ret;
408 }
409 #elif defined (ANDROID_KEYSTORE)
410 #include <cutils/sockets.h>
411 #include <keystore.h>
412 char *keystore_strerror(int err)
413 {
414         switch (-err) {
415         case NO_ERROR:          return _("No error");
416         case LOCKED:            return _("Keystore ocked");
417         case UNINITIALIZED:     return _("Keystore uninitialized");
418         case SYSTEM_ERROR:      return _("System error");
419         case PROTOCOL_ERROR:    return _("Protocol error");
420         case PERMISSION_DENIED: return _("Permission denied");
421         case KEY_NOT_FOUND:     return _("Key not found");
422         case VALUE_CORRUPTED:   return _("Value corrupted");
423         case UNDEFINED_ACTION:  return _("Undefined action");
424         case WRONG_PASSWORD_0:
425         case WRONG_PASSWORD_1:
426         case WRONG_PASSWORD_2:
427         case WRONG_PASSWORD_3:  return _("Wrong password");
428         default:                return _("Unknown error");
429         }
430 }
431
432 /* Returns length, or a negative errno in its own namespace (handled by its
433    own strerror function above). The numbers are from Android's keystore.h */
434 int keystore_fetch(const char *key, unsigned char **result)
435 {
436         unsigned char *data, *p;
437         unsigned char buf[3];
438         int len, fd, ofs;
439         int ret = -SYSTEM_ERROR;
440
441         fd = socket_local_client("keystore",
442                                  ANDROID_SOCKET_NAMESPACE_RESERVED,
443                                  SOCK_STREAM);
444         if (fd < 0)
445                 return -SYSTEM_ERROR;
446
447         len = strlen(key);
448         buf[0] = 'g';
449         buf[1] = len >> 8;
450         buf[2] = len & 0xff;
451
452         if (send(fd, buf, 3, 0) != 3 || send(fd, key, len, 0) != len ||
453             shutdown(fd, SHUT_WR) || recv(fd, buf, 1, 0) != 1)
454                 goto out;
455
456         if (buf[0] != NO_ERROR) {
457                 /* Should never be zero */
458                 ret = buf[0] ? -buf[0] : -PROTOCOL_ERROR;
459                 goto out;
460         }
461         if (recv(fd, buf, 2, 0) != 2)
462                 goto out;
463         len = (buf[0] << 8) + buf[1];
464         data = malloc(len);
465         if (!data)
466                 goto out;
467         p  = data;
468         ret = len;
469         while (len) {
470                 int got = recv(fd, p, len, 0);
471                 if (got <= 0) {
472                         free(data);
473                         ret = -PROTOCOL_ERROR;
474                         goto out;
475                 }
476                 len -= got;
477                 p += got;
478         }
479
480         *result = data;
481
482  out:
483         close(fd);
484         return ret;
485 }
486 #endif