Add client certificate support for GnuTLS
[platform/upstream/openconnect.git] / gnutls.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/stat.h>
27 #include <sys/socket.h>
28 #include <netdb.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <netinet/in.h>
35 #include <arpa/inet.h>
36 #include <errno.h>
37 #include <stdarg.h>
38 #include <stdlib.h>
39
40 #include <gnutls/gnutls.h>
41 #include <gnutls/x509.h>
42 #include <gnutls/crypto.h>
43 #include <gnutls/pkcs12.h>
44
45 #include "openconnect-internal.h"
46
47 /* OSX < 1.6 doesn't have AI_NUMERICSERV */
48 #ifndef AI_NUMERICSERV
49 #define AI_NUMERICSERV 0
50 #endif
51
52 /* Helper functions for reading/writing lines over SSL.
53    We could use cURL for the HTTP stuff, but it's overkill */
54
55 int openconnect_SSL_write(struct openconnect_info *vpninfo, char *buf, size_t len)
56 {
57         size_t orig_len = len;
58
59         while (len) {
60                 int done = gnutls_record_send(vpninfo->https_sess, buf, len);
61                 if (done > 0)
62                         len -= done;
63                 else if (done != GNUTLS_E_AGAIN) {
64                         vpn_progress(vpninfo, PRG_ERR, _("Failed to write to SSL socket: %s"),
65                                      gnutls_strerror(done));
66                         return -EIO;
67                 } else {
68                         fd_set wr_set, rd_set;
69                         int maxfd = vpninfo->ssl_fd;
70
71                         FD_ZERO(&wr_set);
72                         FD_ZERO(&rd_set);
73                         
74                         if (gnutls_record_get_direction(vpninfo->https_sess))
75                                 FD_SET(vpninfo->ssl_fd, &wr_set);
76                         else
77                                 FD_SET(vpninfo->ssl_fd, &rd_set);
78
79                         if (vpninfo->cancel_fd != -1) {
80                                 FD_SET(vpninfo->cancel_fd, &rd_set);
81                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
82                                         maxfd = vpninfo->cancel_fd;
83                         }
84                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
85                         if (vpninfo->cancel_fd != -1 &&
86                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
87                                 vpn_progress(vpninfo, PRG_ERR, _("SSL write cancelled\n"));
88                                 return -EINTR;
89                         }
90                 }
91         }
92         return orig_len;
93 }
94
95 int openconnect_SSL_read(struct openconnect_info *vpninfo, char *buf, size_t len)
96 {
97         int done;
98
99         while ((done = gnutls_record_recv(vpninfo->https_sess, buf, len)) < 0) {
100                 fd_set wr_set, rd_set;
101                 int maxfd = vpninfo->ssl_fd;
102
103                 if (done != GNUTLS_E_AGAIN) {
104                         vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket: %s"),
105                                      gnutls_strerror(done));
106                         return -EIO;
107                 } else {
108                         FD_ZERO(&wr_set);
109                         FD_ZERO(&rd_set);
110                         
111                         if (gnutls_record_get_direction(vpninfo->https_sess))
112                                 FD_SET(vpninfo->ssl_fd, &wr_set);
113                         else
114                                 FD_SET(vpninfo->ssl_fd, &rd_set);
115
116                         if (vpninfo->cancel_fd != -1) {
117                                 FD_SET(vpninfo->cancel_fd, &rd_set);
118                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
119                                         maxfd = vpninfo->cancel_fd;
120                         }
121                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
122                         if (vpninfo->cancel_fd != -1 &&
123                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
124                                 vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
125                                 return -EINTR;
126                         }
127                 }
128         }
129         return done;
130 }
131
132 int  __attribute__ ((format (printf, 2, 3)))
133     openconnect_SSL_printf(struct openconnect_info *vpninfo, const char *fmt, ...)
134 {
135         char buf[1024];
136         va_list args;
137
138         buf[1023] = 0;
139
140         va_start(args, fmt);
141         vsnprintf(buf, 1023, fmt, args);
142         va_end(args);
143         return openconnect_SSL_write(vpninfo, buf, strlen(buf));
144
145 }
146
147 int openconnect_SSL_gets(struct openconnect_info *vpninfo, char *buf, size_t len)
148 {
149         int i = 0;
150         int ret;
151
152         if (len < 2)
153                 return -EINVAL;
154
155         while (1) {
156                 ret = gnutls_record_recv(vpninfo->https_sess, buf + i, 1);
157                 if (ret == 1) {
158                         if (buf[i] == '\n') {
159                                 buf[i] = 0;
160                                 if (i && buf[i-1] == '\r') {
161                                         buf[i-1] = 0;
162                                         i--;
163                                 }
164                                 return i;
165                         }
166                         i++;
167
168                         if (i >= len - 1) {
169                                 buf[i] = 0;
170                                 return i;
171                         }
172                 } else if (ret != GNUTLS_E_AGAIN) {
173                         vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket: %s\n"),
174                                      gnutls_strerror(ret));
175                         ret = -EIO;
176                         break;
177                 } else {
178                         fd_set rd_set, wr_set;
179                         int maxfd = vpninfo->ssl_fd;
180                         
181                         FD_ZERO(&rd_set);
182                         FD_ZERO(&wr_set);
183                         
184                         if (gnutls_record_get_direction(vpninfo->https_sess))
185                                 FD_SET(vpninfo->ssl_fd, &wr_set);
186                         else
187                                 FD_SET(vpninfo->ssl_fd, &rd_set);
188
189                         if (vpninfo->cancel_fd != -1) {
190                                 FD_SET(vpninfo->cancel_fd, &rd_set);
191                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
192                                         maxfd = vpninfo->cancel_fd;
193                         }
194                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
195                         if (vpninfo->cancel_fd != -1 &&
196                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
197                                 vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
198                                 ret = -EINTR;
199                                 break;
200                         }
201                 }
202         }
203         buf[i] = 0;
204         return i ?: ret;
205 }
206
207 static int request_passphrase(struct openconnect_info *vpninfo,
208                               char **response, const char *fmt, ...)
209 {
210         struct oc_auth_form f;
211         struct oc_form_opt o;
212         char buf[1024];
213         va_list args;
214         int ret;
215
216         buf[1023] = 0;
217         memset(&f, 0, sizeof(f));
218         va_start(args, fmt);
219         vsnprintf(buf, 1023, fmt, args);
220         va_end(args);
221
222         f.auth_id = (char *)"gnutls_certificate";
223         f.opts = &o;
224
225         o.next = NULL;
226         o.type = OC_FORM_OPT_PASSWORD;
227         o.name = (char *)"passphrase";
228         o.label = buf;
229         o.value = NULL;
230
231         ret = vpninfo->process_auth_form(vpninfo, &f);
232         if (!ret) {
233                 *response = o.value;
234                 return 0;
235         }
236
237         return -EIO;
238 }
239
240 static int load_datum(struct openconnect_info *vpninfo,
241                       gnutls_datum_t *datum, const char *fname)
242 {
243         struct stat st;
244         int fd, err;
245
246         fd = open(fname, O_RDONLY|O_CLOEXEC);
247         if (fd == -1) {
248                 err = errno;
249                 vpn_progress(vpninfo, PRG_ERR,
250                              _("Failed to open certificate file %s: %s\n"),
251                              vpninfo->cert, strerror(err));
252                 return -ENOENT;
253         }
254         if (fstat(fd, &st)) {
255                 err = errno;
256                 vpn_progress(vpninfo, PRG_ERR,
257                              _("Failed to stat certificate file %s: %s\n"),
258                              vpninfo->cert, strerror(err));
259                 close(fd);
260                 return -EIO;
261         }                       
262         datum->size = st.st_size;
263         datum->data = gnutls_malloc(st.st_size);
264         if (!datum->data) {
265                 vpn_progress(vpninfo, PRG_ERR,
266                              _("Failed to allocate certificate buffer\n"));
267                 close(fd);
268                 return -ENOMEM;
269         }
270         errno = EAGAIN;
271         if (read(fd, datum->data, datum->size) != datum->size) {
272                 err = errno;
273                 vpn_progress(vpninfo, PRG_ERR,
274                              _("Failed to read certificate into memory: %s\n"),
275                              strerror(err));
276                 close(fd);
277                 gnutls_free(datum->data);
278                 return -EIO;
279         }
280         close(fd);
281         return 0;
282 }
283
284 static int load_pkcs12_certificate(struct openconnect_info *vpninfo,
285                                    gnutls_datum_t *datum)
286 {
287         gnutls_pkcs12_t p12;
288         char *pass;
289         int err;
290
291         err = gnutls_pkcs12_init(&p12);
292         if (err) {
293                 vpn_progress(vpninfo, PRG_ERR,
294                              _("Failed to setup PKCS#12 data structure: %s\n"),
295                              gnutls_strerror(err));
296                 return -EIO;
297         }
298
299         err = gnutls_pkcs12_import(p12, datum, GNUTLS_X509_FMT_DER, 0);
300         if (err) {
301                 gnutls_pkcs12_deinit(p12);
302                 if (vpninfo->cert_type == CERT_TYPE_UNKNOWN)
303                         return 1; /* Try PEM */
304                 vpn_progress(vpninfo, PRG_ERR,
305                              _("Failed to import PKCS#12 file: %s\n"),
306                              gnutls_strerror(err));
307                 return -EINVAL;
308         }
309
310         pass = vpninfo->cert_password;
311         while (gnutls_pkcs12_verify_mac(p12, pass)) {
312                 if (pass)
313                         vpn_progress(vpninfo, PRG_ERR,
314                                      _("Failed to decrypt PKCS#12 certificate file\n"));
315                 free(pass);
316                 err = request_passphrase(vpninfo, &pass,
317                                          _("Enter PKCS#12 pass phrase:"));
318                 if (err) {
319                         gnutls_pkcs12_deinit(p12);
320                         return -EINVAL;
321                 }
322         }
323         /* We can't actually *use* this gnutls_pkcs12_t, AFAICT.
324            We have to let GnuTLS re-import it all again. */
325         gnutls_pkcs12_deinit(p12);
326
327         err = gnutls_certificate_set_x509_simple_pkcs12_mem(vpninfo->https_cred, datum,
328                                                             GNUTLS_X509_FMT_DER, pass);
329                 
330         if (err) {
331                 vpn_progress(vpninfo, PRG_ERR,
332                              _("Failed to load PKCS#12 certificate: %s\n"),
333                              gnutls_strerror(err));
334                 return -EINVAL;
335         }
336         return 0;
337 }
338
339 static int load_certificate(struct openconnect_info *vpninfo)
340 {
341         gnutls_datum_t fdata;
342         gnutls_x509_crt_t cert;
343         gnutls_x509_privkey_t key;
344         int err;
345
346         if (vpninfo->cert_type == CERT_TYPE_TPM) {
347                 vpn_progress(vpninfo, PRG_ERR,
348                              _("TPM support not available with GnuTLS\n"));
349                 return -EINVAL;
350         }
351
352         if (!strncmp(vpninfo->cert, "pkcs11:", 7)) {
353                 vpn_progress(vpninfo, PRG_TRACE,
354                              _("Using PKCS#11 certificate %s\n"), vpninfo->cert);
355
356                 err = gnutls_certificate_set_x509_key_file(vpninfo->https_cred,
357                                                            vpninfo->cert, 
358                                                            vpninfo->sslkey,
359                                                            GNUTLS_X509_FMT_PEM);
360                 if (err) {
361                         vpn_progress(vpninfo, PRG_ERR,
362                                      _("Error loading PKCS#11 certificate: %s\n"),
363                                      gnutls_strerror(err));
364                         return -EIO;
365                 }
366                 return 0;
367         }
368                 
369         vpn_progress(vpninfo, PRG_TRACE,
370                      _("Using certificate file %s\n"), vpninfo->cert);
371         
372         err = load_datum(vpninfo, &fdata, vpninfo->cert);
373         if (err)
374                 return err;
375
376         if (vpninfo->cert_type == CERT_TYPE_PKCS12 ||
377             vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
378                 err = load_pkcs12_certificate(vpninfo, &fdata);
379                 /* Either it's printed and error and failed, or it's succeeded */
380                 if (err <= 0) {
381                         gnutls_free(fdata.data);
382                         return err;
383                 }
384                 /* ... or it falls through to try PEM formats */
385         }
386
387         gnutls_x509_crt_init(&cert);
388         err = gnutls_x509_crt_import(cert, &fdata, GNUTLS_X509_FMT_PEM);
389         if (err) {
390                 vpn_progress(vpninfo, PRG_ERR,
391                              _("Loading certificate failed: %s\n"),
392                              gnutls_strerror(err));
393                 gnutls_free(fdata.data);
394                 return -EINVAL;
395         }
396
397         if (vpninfo->sslkey != vpninfo->cert) {
398                 gnutls_free(fdata.data);
399
400                 vpn_progress(vpninfo, PRG_TRACE,
401                              _("Using private key file %s\n"), vpninfo->cert);
402
403                 err = load_datum(vpninfo, &fdata, vpninfo->sslkey);
404                 if (err) {
405                         gnutls_x509_crt_deinit(cert);
406                         return err;
407                 }
408         }
409         
410         gnutls_x509_privkey_init(&key);
411         /* Try PKCS#1 (and PKCS#8 without password) first. GnuTLS doesn't
412            support OpenSSL's old PKCS#1-based encrypted format. We should
413            probably check for it and give a more coherent failure mode. */
414         err = gnutls_x509_privkey_import(key, &fdata, GNUTLS_X509_FMT_PEM);
415         if (err) {
416                 /* If that fails, try PKCS#8 */
417                 char *pass = vpninfo->cert_password;
418
419                 /* Yay, just for fun this is *different* to PKCS#12. Where we could
420                    try an empty password there, in this case the empty-password case
421                    has already been *tried* by gnutls_x509_privkey_import(). If we
422                    just call gnutls_x509_privkey_import_pkcs8() with a NULL password,
423                    it'll SEGV. You have to set the GNUTLS_PKCS_PLAIN flag if you want
424                    to try without a password. Passing NULL evidently isn't enough of
425                    a hint. */
426                 while ((err = gnutls_x509_privkey_import_pkcs8(key, &fdata,
427                                                                GNUTLS_X509_FMT_PEM,
428                                                                pass?pass:"", 0))) {
429                         if (err != GNUTLS_E_DECRYPTION_FAILED) {
430                                 vpn_progress(vpninfo, PRG_ERR,
431                                              _("Failed to load private key as PKCS#8: %s\n"),
432                                              gnutls_strerror(err));
433                                 gnutls_x509_crt_deinit(cert);
434                                 gnutls_free(fdata.data);
435                                 return -EINVAL;
436                         }
437                         if (pass) {
438                                 vpn_progress(vpninfo, PRG_ERR,
439                                              _("Failed to decrypt PKCS#8 certificate file\n"));
440                                 free (pass);
441                         }
442                         err = request_passphrase(vpninfo, &pass,
443                                                  _("Enter PEM pass phrase:"));
444                         if (err) {
445                                 gnutls_x509_crt_deinit(cert);
446                                 gnutls_free(fdata.data);
447                                 return -EINVAL;
448                         }
449                 }
450         }
451         /* FIXME: We need to work around OpenSSL RT#1942 on the server, by including
452            as much of the chain of issuer certificates as we can. */
453         err = gnutls_certificate_set_x509_key(vpninfo->https_cred,
454                                               &cert, 1, key);
455         gnutls_x509_privkey_deinit(key);
456         gnutls_x509_crt_deinit(cert);
457         gnutls_free(fdata.data);
458         if (err) {
459                 vpn_progress(vpninfo, PRG_ERR,
460                              _("Setting certificate failed: %s\n"),
461                              gnutls_strerror(err));
462                 return -EIO;
463         }
464         return 0;
465 }
466
467 static int get_cert_fingerprint(struct openconnect_info *vpninfo,
468                                 gnutls_x509_crt_t cert,
469                                 gnutls_digest_algorithm_t algo,
470                                 char *buf)
471 {
472         unsigned char md[256];
473         size_t md_size = sizeof(md);
474         unsigned int i;
475
476         if (gnutls_x509_crt_get_fingerprint(cert, algo, md, &md_size))
477                 return -EIO;
478
479         for (i=0; i < md_size; i++)
480                 sprintf(&buf[i*2], "%02X", md[i]);
481
482         return 0;
483 }
484
485 int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
486                              OPENCONNECT_X509 *cert, char *buf)
487 {
488         return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_MD5, buf);
489 }
490
491 int openconnect_get_cert_sha1(struct openconnect_info *vpninfo,
492                               OPENCONNECT_X509 *cert, char *buf)
493 {
494         return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_SHA1, buf);
495 }
496
497 static int check_server_cert(struct openconnect_info *vpninfo, OPENCONNECT_X509 *cert)
498 {
499         char fingerprint[41];
500         int ret;
501
502         ret = openconnect_get_cert_sha1(vpninfo, cert, fingerprint);
503         if (ret)
504                 return ret;
505
506         if (strcasecmp(vpninfo->servercert, fingerprint)) {
507                 vpn_progress(vpninfo, PRG_ERR,
508                              _("Server SSL certificate didn't match: %s\n"), fingerprint);
509                 return -EINVAL;
510         }
511         return 0;
512 }
513
514 char *openconnect_get_cert_details(struct openconnect_info *vpninfo,
515                                    OPENCONNECT_X509 *cert)
516 {
517         gnutls_datum_t buf;
518         char *ret;
519
520         if (gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &buf))
521                 return NULL;
522         
523         /* Just in case gnutls_free() isn't free(), we can't steal it. */
524         ret = strdup((char *)buf.data);
525         gnutls_free(buf.data);
526         
527         return ret;
528 }
529
530 int openconnect_get_cert_DER(struct openconnect_info *vpninfo,
531                              OPENCONNECT_X509 *cert, unsigned char **buf)
532 {
533         size_t l = 0;
534         unsigned char *ret = NULL;
535
536         if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l) != 
537             GNUTLS_E_SHORT_MEMORY_BUFFER)
538                 return -EIO;
539
540         ret = malloc(l);
541         if (!ret)
542                 return -ENOMEM;
543
544         if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l)) {
545                 free(ret);
546                 return -EIO;
547         }
548         *buf = ret;
549         return l;
550 }
551 #if 0
552 static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl)
553 {
554         X509 *peer_cert;
555         int ret;
556
557         peer_cert = SSL_get_peer_certificate(https_ssl);
558
559         if (vpninfo->servercert) {
560                 /* If given a cert fingerprint on the command line, that's
561                    all we look for */
562                 ret = check_server_cert(vpninfo, peer_cert);
563         } else {
564                 int vfy = SSL_get_verify_result(https_ssl);
565                 const char *err_string = NULL;
566
567                 if (vfy != X509_V_OK)
568                         err_string = X509_verify_cert_error_string(vfy);
569                 else if (match_cert_hostname(vpninfo, peer_cert))
570                         err_string = _("certificate does not match hostname");
571
572                 if (err_string) {
573                         vpn_progress(vpninfo, PRG_INFO,
574                                      _("Server certificate verify failed: %s\n"),
575                                      err_string);
576
577                         if (vpninfo->validate_peer_cert)
578                                 ret = vpninfo->validate_peer_cert(vpninfo->cbdata,
579                                                                   peer_cert,
580                                                                   err_string);
581                         else
582                                 ret = -EINVAL;
583                 } else {
584                         ret = 0;
585                 }
586         }
587         X509_free(peer_cert);
588
589         return ret;
590 }
591 #endif
592 static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo)
593 {
594         /* OpenSSL has problems with certificate chains -- if there are
595            multiple certs with the same name, it doesn't necessarily
596            choose the _right_ one. (RT#1942)
597            Pick the right ones for ourselves and add them manually. */
598         
599         /* FIXME: Of course we still have to do this with GnuTLS, to work
600            around the issue on the server side */
601 }
602
603 static int check_certificate_expiry(struct openconnect_info *vpninfo)
604 {
605         /* FIXME */
606         return 0;
607 #if 0
608         ASN1_TIME *notAfter;
609         const char *reason = NULL;
610         time_t t;
611         int i;
612
613         if (!vpninfo->cert_x509)
614                 return 0;
615
616         t = time(NULL);
617         notAfter = X509_get_notAfter(vpninfo->cert_x509);
618         i = X509_cmp_time(notAfter, &t);
619         if (!i) {
620                 vpn_progress(vpninfo, PRG_ERR,
621                              _("Error in client cert notAfter field\n"));
622                 return -EINVAL;
623         } else if (i < 0) {
624                 reason = _("Client certificate has expired at");
625         } else {
626                 t += vpninfo->cert_expire_warning;
627                 i = X509_cmp_time(notAfter, &t);
628                 if (i < 0) {
629                         reason = _("Client certificate expires soon at");
630                 }
631         }
632         if (reason) {
633                 BIO *bp = BIO_new(BIO_s_mem());
634                 BUF_MEM *bm;
635                 const char *expiry = _("<error>");
636                 char zero = 0;
637
638                 if (bp) {
639                         ASN1_TIME_print(bp, notAfter);
640                         BIO_write(bp, &zero, 1);
641                         BIO_get_mem_ptr(bp, &bm);
642                         expiry = bm->data;
643                 }
644                 vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", reason, expiry);
645                 if (bp)
646                         BIO_free(bp);
647         }
648         return 0;
649 #endif
650 }
651
652 static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
653                                const struct sockaddr *addr, socklen_t addrlen)
654 {
655         struct sockaddr_storage peer;
656         socklen_t peerlen = sizeof(peer);
657         fd_set wr_set, rd_set;
658         int maxfd = sockfd;
659
660         fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
661
662         if (connect(sockfd, addr, addrlen) < 0 && errno != EINPROGRESS)
663                 return -1;
664
665         FD_ZERO(&wr_set);
666         FD_ZERO(&rd_set);
667         FD_SET(sockfd, &wr_set);
668         if (vpninfo->cancel_fd != -1) {
669                 FD_SET(vpninfo->cancel_fd, &rd_set);
670                 if (vpninfo->cancel_fd > sockfd)
671                         maxfd = vpninfo->cancel_fd;
672         }
673         
674         /* Later we'll render this whole exercise non-pointless by
675            including a 'cancelfd' here too. */
676         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
677         if (vpninfo->cancel_fd != -1 && FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
678                 vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n"));
679                 errno = EINTR;
680                 return -1;
681         }
682                 
683         /* Check whether connect() succeeded or failed by using
684            getpeername(). See http://cr.yp.to/docs/connect.html */
685         return getpeername(sockfd, (void *)&peer, &peerlen);
686 }
687
688 int openconnect_open_https(struct openconnect_info *vpninfo)
689 {
690         int ssl_sock = -1;
691         int err;
692
693         if (vpninfo->https_sess)
694                 return 0;
695
696         if (!vpninfo->port)
697                 vpninfo->port = 443;
698
699         if (vpninfo->peer_addr) {
700 #ifdef SOCK_CLOEXEC
701                 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
702                 if (ssl_sock < 0)
703 #endif
704                 {
705                         ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
706                         if (ssl_sock < 0)
707                                 goto reconn_err;
708                         fcntl(ssl_sock, F_SETFD, fcntl(ssl_sock, F_GETFD) | FD_CLOEXEC);
709                 }
710                 if (cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen)) {
711                 reconn_err:
712                         if (vpninfo->proxy) {
713                                 vpn_progress(vpninfo, PRG_ERR, 
714                                              _("Failed to reconnect to proxy %s\n"),
715                                              vpninfo->proxy);
716                         } else {
717                                 vpn_progress(vpninfo, PRG_ERR, 
718                                              _("Failed to reconnect to host %s\n"),
719                                              vpninfo->hostname);
720                         }
721                         return -EINVAL;
722                 }
723                 
724         } else {
725                 struct addrinfo hints, *result, *rp;
726                 char *hostname;
727                 char port[6];
728
729                 memset(&hints, 0, sizeof(struct addrinfo));
730                 hints.ai_family = AF_UNSPEC;
731                 hints.ai_socktype = SOCK_STREAM;
732                 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
733                 hints.ai_protocol = 0;
734                 hints.ai_canonname = NULL;
735                 hints.ai_addr = NULL;
736                 hints.ai_next = NULL;
737
738                 /* The 'port' variable is a string because it's easier
739                    this way than if we pass NULL to getaddrinfo() and
740                    then try to fill in the numeric value into
741                    different types of returned sockaddr_in{6,}. */
742 #ifdef LIBPROXY_HDR
743                 if (vpninfo->proxy_factory) {
744                         char *url;
745                         char **proxies;
746                         int i = 0;
747
748                         free(vpninfo->proxy_type);
749                         vpninfo->proxy_type = NULL;
750                         free(vpninfo->proxy);
751                         vpninfo->proxy = NULL;
752
753                         if (vpninfo->port == 443)
754                                 i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
755                                              vpninfo->urlpath?:"");
756                         else
757                                 i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
758                                              vpninfo->port, vpninfo->urlpath?:"");
759                         if (i == -1)
760                                 return -ENOMEM;
761
762                         proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
763                                                                url);
764
765                         i = 0;
766                         while (proxies && proxies[i]) {
767                                 if (!vpninfo->proxy &&
768                                     (!strncmp(proxies[i], "http://", 7) ||
769                                      !strncmp(proxies[i], "socks://", 8) ||
770                                      !strncmp(proxies[i], "socks5://", 9)))
771                                         internal_parse_url(proxies[i], &vpninfo->proxy_type,
772                                                   &vpninfo->proxy, &vpninfo->proxy_port,
773                                                   NULL, 0);
774                                 i++;
775                         }
776                         free(url);
777                         free(proxies);
778                         if (vpninfo->proxy)
779                                 vpn_progress(vpninfo, PRG_TRACE,
780                                              _("Proxy from libproxy: %s://%s:%d/\n"),
781                                              vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
782                 }
783 #endif
784                 if (vpninfo->proxy) {
785                         hostname = vpninfo->proxy;
786                         snprintf(port, 6, "%d", vpninfo->proxy_port);
787                 } else {
788                         hostname = vpninfo->hostname;
789                         snprintf(port, 6, "%d", vpninfo->port);
790                 }
791
792                 if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
793                         /* Solaris has no strndup(). */
794                         int len = strlen(hostname) - 2;
795                         char *new_hostname = malloc(len + 1);
796                         if (!new_hostname)
797                                 return -ENOMEM;
798                         memcpy(new_hostname, hostname + 1, len);
799                         new_hostname[len] = 0;
800
801                         hostname = new_hostname;
802                         hints.ai_flags |= AI_NUMERICHOST;
803                 }
804
805                 err = getaddrinfo(hostname, port, &hints, &result);
806                 if (hints.ai_flags & AI_NUMERICHOST)
807                         free(hostname);
808
809                 if (err) {
810                         vpn_progress(vpninfo, PRG_ERR,
811                                      _("getaddrinfo failed for host '%s': %s\n"),
812                                      hostname, gai_strerror(err));
813                         return -EINVAL;
814                 }
815
816                 for (rp = result; rp ; rp = rp->ai_next) {
817                         char host[80];
818
819                         if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
820                                          sizeof(host), NULL, 0, NI_NUMERICHOST))
821                                 vpn_progress(vpninfo, PRG_INFO,
822                                              _("Attempting to connect to %s%s%s:%s\n"),
823                                              rp->ai_family == AF_INET6?"[":"",
824                                              host,
825                                              rp->ai_family == AF_INET6?"]":"",
826                                              port);
827                         
828                         ssl_sock = socket(rp->ai_family, rp->ai_socktype,
829                                           rp->ai_protocol);
830                         if (ssl_sock < 0)
831                                 continue;
832                         if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
833                                 /* Store the peer address we actually used, so that DTLS can
834                                    use it again later */
835                                 vpninfo->peer_addr = malloc(rp->ai_addrlen);
836                                 if (!vpninfo->peer_addr) {
837                                         vpn_progress(vpninfo, PRG_ERR,
838                                                      _("Failed to allocate sockaddr storage\n"));
839                                         close(ssl_sock);
840                                         return -ENOMEM;
841                                 }
842                                 vpninfo->peer_addrlen = rp->ai_addrlen;
843                                 memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
844                                 break;
845                         }
846                         close(ssl_sock);
847                         ssl_sock = -1;
848                 }
849                 freeaddrinfo(result);
850                 
851                 if (ssl_sock < 0) {
852                         vpn_progress(vpninfo, PRG_ERR,
853                                      _("Failed to connect to host %s\n"),
854                                      vpninfo->proxy?:vpninfo->hostname);
855                         return -EINVAL;
856                 }
857         }
858
859         if (vpninfo->proxy) {
860                 err = process_proxy(vpninfo, ssl_sock);
861                 if (err) {
862                         close(ssl_sock);
863                         return err;
864                 }
865         }
866
867         if (!vpninfo->https_cred) {
868                 gnutls_certificate_allocate_credentials(&vpninfo->https_cred);
869                 gnutls_certificate_set_x509_trust_file(vpninfo->https_cred,
870                                                        "/etc/pki/tls/certs/ca-bundle.crt",
871                                                        GNUTLS_X509_FMT_PEM);
872
873                 /* FIXME: Ensure TLSv1.0, no options */
874
875                 if (vpninfo->cert) {
876                         err = load_certificate(vpninfo);
877                         if (err) {
878                                 vpn_progress(vpninfo, PRG_ERR,
879                                              _("Loading certificate failed. Aborting.\n"));
880                                 return err;
881                         }
882                         check_certificate_expiry(vpninfo);
883                 }
884
885                 /* We just want to do:
886                    SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY); 
887                    ... but it doesn't work with OpenSSL < 0.9.8k because of 
888                    problems with inheritance (fixed in v1.1.4.6 of
889                    crypto/x509/x509_vpm.c) so we have to play silly buggers
890                    instead. This trick doesn't work _either_ in < 0.9.7 but
891                    I don't know of _any_ workaround which will, and can't
892                    be bothered to find out either. */
893
894                 if (vpninfo->cafile) {
895                         err = gnutls_certificate_set_x509_trust_file(vpninfo->https_cred,
896                                                                      vpninfo->cafile,
897                                                                      GNUTLS_X509_FMT_PEM);
898                         if (err) {
899                                 vpn_progress(vpninfo, PRG_ERR,
900                                              _("Failed to open CA file '%s': %s\n"),
901                                              vpninfo->cafile, gnutls_strerror(err));
902                                 close(ssl_sock);
903                                 return -EINVAL;
904                         }
905                 }
906
907         }
908         gnutls_init (&vpninfo->https_sess, GNUTLS_CLIENT);
909         err = gnutls_priority_set_direct (vpninfo->https_sess, "NONE:+VERS-TLS1.0:+SHA1:+AES-128-CBC:+RSA:+COMP-NULL:%COMPAT:%DISABLE_SAFE_RENEGOTIATION", NULL);
910         if (err) {
911                 vpn_progress(vpninfo, PRG_ERR,
912                              _("Failed to set TLS priority string: %s\n"),
913                              gnutls_strerror(err));
914                 return -EIO;
915         }
916
917         gnutls_record_disable_padding (vpninfo->https_sess);
918         workaround_openssl_certchain_bug(vpninfo);
919         gnutls_credentials_set (vpninfo->https_sess, GNUTLS_CRD_CERTIFICATE, vpninfo->https_cred);
920         gnutls_transport_set_ptr(vpninfo->https_sess, /* really? */(gnutls_transport_ptr_t)(long) ssl_sock);
921
922         vpn_progress(vpninfo, PRG_INFO, _("SSL negotiation with %s\n"),
923                      vpninfo->hostname);
924
925         while ((err = gnutls_handshake (vpninfo->https_sess))) {
926                 if (err == GNUTLS_E_AGAIN) {
927                         fd_set rd_set, wr_set;
928                         int maxfd = ssl_sock;
929                         
930                         FD_ZERO(&rd_set);
931                         FD_ZERO(&wr_set);
932                         
933                         if (gnutls_record_get_direction(vpninfo->https_sess))
934                                 FD_SET(ssl_sock, &wr_set);
935                         else
936                                 FD_SET(ssl_sock, &rd_set);
937
938                         if (vpninfo->cancel_fd != -1) {
939                                 FD_SET(vpninfo->cancel_fd, &rd_set);
940                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
941                                         maxfd = vpninfo->cancel_fd;
942                         }
943                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
944                         if (vpninfo->cancel_fd != -1 &&
945                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
946                                 vpn_progress(vpninfo, PRG_ERR, _("SSL connection cancelled\n"));
947                                 gnutls_deinit(vpninfo->https_sess);
948                                 vpninfo->https_sess = NULL;
949                                 close(ssl_sock);
950                                 return -EINTR;
951                         }
952                 } else if (err == GNUTLS_E_INTERRUPTED || gnutls_error_is_fatal(err)) {
953                         vpn_progress(vpninfo, PRG_ERR, _("SSL connection failure: %s\n"),
954                                                          gnutls_strerror(err));
955                         gnutls_deinit(vpninfo->https_sess);
956                         vpninfo->https_sess = NULL;
957                         close(ssl_sock);
958                         return -EIO;
959                 } else {
960                         /* non-fatal error or warning. Ignore it and continue */
961                         vpn_progress(vpninfo, PRG_TRACE,
962                                      _("GnuTLS non-fatal return during handshake: %s\n"),
963                                      gnutls_strerror(err));
964                 }
965         }
966 #if 0
967         if (verify_peer(vpninfo, https_ssl)) {
968                 SSL_free(https_ssl);
969                 close(ssl_sock);
970                 return -EINVAL;
971         }
972 #endif
973         vpninfo->ssl_fd = ssl_sock;
974
975         vpn_progress(vpninfo, PRG_INFO, _("Connected to HTTPS on %s\n"),
976                      vpninfo->hostname);
977
978         return 0;
979 }
980
981 void openconnect_close_https(struct openconnect_info *vpninfo)
982 {
983 #if 0
984         if (vpninfo->peer_cert) {
985                 X509_free(vpninfo->peer_cert);
986                 vpninfo->peer_cert = NULL;
987         }
988 #endif
989         if (vpninfo->https_sess) {
990                 gnutls_deinit(vpninfo->https_sess);
991                 vpninfo->https_sess = NULL;
992         }
993         if (vpninfo->ssl_fd != -1) {
994                 close(vpninfo->ssl_fd);
995                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_rfds);
996                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
997                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_efds);
998                 vpninfo->ssl_fd = -1;
999         }
1000 }
1001
1002 void openconnect_init_openssl(void)
1003 {
1004         gnutls_global_init();
1005 }
1006
1007 int openconnect_sha1(unsigned char *result, void *data, int datalen)
1008 {
1009         gnutls_datum_t d;
1010         size_t shalen = SHA1_SIZE;
1011
1012         d.data = data;
1013         d.size = datalen;
1014         if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &d, result, &shalen))
1015                 return -1;
1016
1017         return 0;
1018 }
1019
1020 int openconnect_random(void *bytes, int len)
1021 {
1022         if (gnutls_rnd(GNUTLS_RND_RANDOM, bytes, len))
1023                 return -EIO;
1024         return 0;
1025 }
1026
1027 int openconnect_local_cert_md5(struct openconnect_info *vpninfo,
1028                                char *buf)
1029 {
1030         const gnutls_datum_t *d;
1031         size_t md5len = 16;
1032
1033         buf[0] = 0;
1034
1035         d = gnutls_certificate_get_ours(vpninfo->https_sess);
1036         if (!d)
1037                 return -EIO;
1038
1039         if (gnutls_fingerprint(GNUTLS_DIG_MD5, d, buf, &md5len))
1040                 return -EIO;
1041
1042         return 0;
1043 }
1044