stoken: Implement new auth form to gather soft token information
[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                         if (ssl_sock >= 0)
123                                 close(ssl_sock);
124                         return -EINVAL;
125                 }
126         } else {
127                 struct addrinfo hints, *result, *rp;
128                 char *hostname;
129                 char port[6];
130
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;
139
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,}. */
144 #ifdef LIBPROXY_HDR
145                 if (vpninfo->proxy_factory) {
146                         char *url;
147                         char **proxies;
148                         int i = 0;
149
150                         free(vpninfo->proxy_type);
151                         vpninfo->proxy_type = NULL;
152                         free(vpninfo->proxy);
153                         vpninfo->proxy = NULL;
154
155                         if (vpninfo->port == 443)
156                                 i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
157                                              vpninfo->urlpath?:"");
158                         else
159                                 i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
160                                              vpninfo->port, vpninfo->urlpath?:"");
161                         if (i == -1)
162                                 return -ENOMEM;
163
164                         proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
165                                                                url);
166
167                         i = 0;
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,
175                                                   NULL, 0);
176                                 i++;
177                         }
178                         free(url);
179                         free(proxies);
180                         if (vpninfo->proxy)
181                                 vpn_progress(vpninfo, PRG_TRACE,
182                                              _("Proxy from libproxy: %s://%s:%d/\n"),
183                                              vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
184                 }
185 #endif
186                 if (vpninfo->proxy) {
187                         hostname = vpninfo->proxy;
188                         snprintf(port, 6, "%d", vpninfo->proxy_port);
189                 } else {
190                         hostname = vpninfo->hostname;
191                         snprintf(port, 6, "%d", vpninfo->port);
192                 }
193
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);
198                         if (!new_hostname)
199                                 return -ENOMEM;
200                         memcpy(new_hostname, hostname + 1, len);
201                         new_hostname[len] = 0;
202
203                         hostname = new_hostname;
204                         hints.ai_flags |= AI_NUMERICHOST;
205                 }
206
207                 err = getaddrinfo(hostname, port, &hints, &result);
208
209                 if (err) {
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)
214                                 free(hostname);
215                         return -EINVAL;
216                 }
217                 if (hints.ai_flags & AI_NUMERICHOST)
218                         free(hostname);
219
220                 for (rp = result; rp ; rp = rp->ai_next) {
221                         char host[80];
222
223                         if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
224                                          sizeof(host), NULL, 0, NI_NUMERICHOST))
225                                 vpn_progress(vpninfo, PRG_INFO, vpninfo->proxy_type?
226                                                      _("Attempting to connect to proxy %s%s%s:%s\n"):
227                                                      _("Attempting to connect to server %s%s%s:%s\n"),
228                                              rp->ai_family == AF_INET6?"[":"",
229                                              host,
230                                              rp->ai_family == AF_INET6?"]":"",
231                                              port);
232
233                         ssl_sock = socket(rp->ai_family, rp->ai_socktype,
234                                           rp->ai_protocol);
235                         if (ssl_sock < 0)
236                                 continue;
237                         if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
238                                 /* Store the peer address we actually used, so that DTLS can
239                                    use it again later */
240                                 vpninfo->peer_addr = malloc(rp->ai_addrlen);
241                                 if (!vpninfo->peer_addr) {
242                                         vpn_progress(vpninfo, PRG_ERR,
243                                                      _("Failed to allocate sockaddr storage\n"));
244                                         close(ssl_sock);
245                                         return -ENOMEM;
246                                 }
247                                 vpninfo->peer_addrlen = rp->ai_addrlen;
248                                 memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
249                                 break;
250                         }
251                         close(ssl_sock);
252                         ssl_sock = -1;
253                 }
254                 freeaddrinfo(result);
255                 
256                 if (ssl_sock < 0) {
257                         vpn_progress(vpninfo, PRG_ERR,
258                                      _("Failed to connect to host %s\n"),
259                                      vpninfo->proxy?:vpninfo->hostname);
260                         return -EINVAL;
261                 }
262         }
263
264         if (vpninfo->proxy) {
265                 err = process_proxy(vpninfo, ssl_sock);
266                 if (err) {
267                         close(ssl_sock);
268                         return err;
269                 }
270         }
271
272         return ssl_sock;
273 }
274
275 int  __attribute__ ((format (printf, 2, 3)))
276     openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...)
277 {
278         char buf[1024];
279         va_list args;
280
281         buf[1023] = 0;
282
283         va_start(args, fmt);
284         vsnprintf(buf, 1023, fmt, args);
285         va_end(args);
286         return openconnect_SSL_write(vpninfo, buf, strlen(buf));
287
288 }
289
290 int request_passphrase(struct openconnect_info *vpninfo, const char *label,
291                        char **response, const char *fmt, ...)
292 {
293         struct oc_auth_form f;
294         struct oc_form_opt o;
295         char buf[1024];
296         va_list args;
297         int ret;
298
299         if (!vpninfo->process_auth_form)
300                 return -EINVAL;
301
302         buf[1023] = 0;
303         memset(&f, 0, sizeof(f));
304         va_start(args, fmt);
305         vsnprintf(buf, 1023, fmt, args);
306         va_end(args);
307
308         f.auth_id = (char *)label;
309         f.opts = &o;
310
311         o.next = NULL;
312         o.type = OC_FORM_OPT_PASSWORD;
313         o.name = (char *)label;
314         o.label = buf;
315         o.value = NULL;
316
317         ret = vpninfo->process_auth_form(vpninfo->cbdata, &f);
318         if (!ret) {
319                 *response = o.value;
320                 return 0;
321         }
322
323         return -EIO;
324 }
325
326 #if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
327 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
328 {
329         struct statvfs buf;
330
331         if (statvfs(vpninfo->sslkey, &buf)) {
332                 int err = errno;
333                 vpn_progress(vpninfo, PRG_ERR, _("statvfs: %s\n"),
334                              strerror(errno));
335                 return -err;
336         }
337         if (asprintf(&vpninfo->cert_password, "%lx", buf.f_fsid))
338                 return -ENOMEM;
339         return 0;
340 }
341 #else
342 int openconnect_passphrase_from_fsid(struct openconnect_info *vpninfo)
343 {
344         struct statfs buf;
345         unsigned *fsid = (unsigned *)&buf.f_fsid;
346         unsigned long long fsid64;
347
348         if (statfs(vpninfo->sslkey, &buf)) {
349                 int err = errno;
350                 vpn_progress(vpninfo, PRG_ERR, _("statfs: %s\n"),
351                              strerror(errno));
352                 return -err;
353         }
354         fsid64 = ((unsigned long long)fsid[0] << 32) | fsid[1];
355
356         if (asprintf(&vpninfo->cert_password, "%llx", fsid64))
357                 return -ENOMEM;
358         return 0;
359 }
360 #endif
361
362 #if defined(OPENCONNECT_OPENSSL) || defined (DTLS_OPENSSL)
363 /* We put this here rather than in openssl.c because it might be needed
364    for OpenSSL DTLS support even when GnuTLS is being used for HTTPS */
365 int openconnect_print_err_cb(const char *str, size_t len, void *ptr)
366 {
367         struct openconnect_info *vpninfo = ptr;
368
369         vpn_progress(vpninfo, PRG_ERR, "%s", str);
370         return 0;
371 }
372 #endif
373
374 #ifdef FAKE_ANDROID_KEYSTORE
375 char *keystore_strerror(int err)
376 {
377         return (char *)strerror(-err);
378 }
379
380 int keystore_fetch(const char *key, unsigned char **result)
381 {
382         unsigned char *data;
383         struct stat st;
384         int fd;
385         int ret;
386
387         fd = open(key, O_RDONLY);
388         if (fd < 0)
389                 return -errno;
390
391         if (fstat(fd, &st)) {
392                 ret = -errno;
393                 goto out_fd;
394         }
395
396         data = malloc(st.st_size + 1);
397         if (!data) {
398                 ret = -ENOMEM;
399                 goto out_fd;
400         }
401
402         if (read(fd, data, st.st_size) != st.st_size) {
403                 ret = -EIO;
404                 free(data);
405                 goto out_fd;
406         }
407
408         data[st.st_size] = 0;
409         *result = data;
410         ret = st.st_size;
411  out_fd:
412         close(fd);
413         return ret;
414 }
415 #elif defined (ANDROID_KEYSTORE)
416 #include <cutils/sockets.h>
417 #include <keystore.h>
418 char *keystore_strerror(int err)
419 {
420         switch (-err) {
421         case NO_ERROR:          return _("No error");
422         case LOCKED:            return _("Keystore ocked");
423         case UNINITIALIZED:     return _("Keystore uninitialized");
424         case SYSTEM_ERROR:      return _("System error");
425         case PROTOCOL_ERROR:    return _("Protocol error");
426         case PERMISSION_DENIED: return _("Permission denied");
427         case KEY_NOT_FOUND:     return _("Key not found");
428         case VALUE_CORRUPTED:   return _("Value corrupted");
429         case UNDEFINED_ACTION:  return _("Undefined action");
430         case WRONG_PASSWORD_0:
431         case WRONG_PASSWORD_1:
432         case WRONG_PASSWORD_2:
433         case WRONG_PASSWORD_3:  return _("Wrong password");
434         default:                return _("Unknown error");
435         }
436 }
437
438 /* Returns length, or a negative errno in its own namespace (handled by its
439    own strerror function above). The numbers are from Android's keystore.h */
440 int keystore_fetch(const char *key, unsigned char **result)
441 {
442         unsigned char *data, *p;
443         unsigned char buf[3];
444         int len, fd, ofs;
445         int ret = -SYSTEM_ERROR;
446
447         fd = socket_local_client("keystore",
448                                  ANDROID_SOCKET_NAMESPACE_RESERVED,
449                                  SOCK_STREAM);
450         if (fd < 0)
451                 return -SYSTEM_ERROR;
452
453         len = strlen(key);
454         buf[0] = 'g';
455         buf[1] = len >> 8;
456         buf[2] = len & 0xff;
457
458         if (send(fd, buf, 3, 0) != 3 || send(fd, key, len, 0) != len ||
459             shutdown(fd, SHUT_WR) || recv(fd, buf, 1, 0) != 1)
460                 goto out;
461
462         if (buf[0] != NO_ERROR) {
463                 /* Should never be zero */
464                 ret = buf[0] ? -buf[0] : -PROTOCOL_ERROR;
465                 goto out;
466         }
467         if (recv(fd, buf, 2, 0) != 2)
468                 goto out;
469         len = (buf[0] << 8) + buf[1];
470         data = malloc(len);
471         if (!data)
472                 goto out;
473         p  = data;
474         ret = len;
475         while (len) {
476                 int got = recv(fd, p, len, 0);
477                 if (got <= 0) {
478                         free(data);
479                         ret = -PROTOCOL_ERROR;
480                         goto out;
481                 }
482                 len -= got;
483                 p += got;
484         }
485
486         *result = data;
487
488  out:
489         close(fd);
490         return ret;
491 }
492 #endif