GnuTLS: Add supporting certificates from PKCS#12 file
[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 check_certificate_expiry(struct openconnect_info *vpninfo, gnutls_x509_crt_t cert)
241 {
242         const char *reason = NULL;
243         time_t expires = gnutls_x509_crt_get_expiration_time(cert);
244         time_t now = time(NULL);
245
246         if (expires == -1) {
247                 vpn_progress(vpninfo, PRG_ERR,
248                              _("Could not extract expiration time of certificate\n"));
249                 return -EINVAL;
250         }
251
252         if (expires < now)
253                 reason = _("Client certificate has expired at");
254         else if (expires < now + vpninfo->cert_expire_warning)
255                 reason = _("Client certificate expires soon at");
256
257         if (reason) {
258                 struct tm tm;
259                 char buf[80];
260
261                 gmtime_r(&expires, &tm);
262                 strftime(buf, 80, "%a, %d %b %Y %T %Z", &tm);
263
264                 vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", reason, buf);
265         }
266         return 0;
267 }
268
269 static int load_datum(struct openconnect_info *vpninfo,
270                       gnutls_datum_t *datum, const char *fname)
271 {
272         struct stat st;
273         int fd, err;
274
275         fd = open(fname, O_RDONLY|O_CLOEXEC);
276         if (fd == -1) {
277                 err = errno;
278                 vpn_progress(vpninfo, PRG_ERR,
279                              _("Failed to open certificate file %s: %s\n"),
280                              vpninfo->cert, strerror(err));
281                 return -ENOENT;
282         }
283         if (fstat(fd, &st)) {
284                 err = errno;
285                 vpn_progress(vpninfo, PRG_ERR,
286                              _("Failed to stat certificate file %s: %s\n"),
287                              vpninfo->cert, strerror(err));
288                 close(fd);
289                 return -EIO;
290         }                       
291         datum->size = st.st_size;
292         datum->data = gnutls_malloc(st.st_size);
293         if (!datum->data) {
294                 vpn_progress(vpninfo, PRG_ERR,
295                              _("Failed to allocate certificate buffer\n"));
296                 close(fd);
297                 return -ENOMEM;
298         }
299         errno = EAGAIN;
300         if (read(fd, datum->data, datum->size) != datum->size) {
301                 err = errno;
302                 vpn_progress(vpninfo, PRG_ERR,
303                              _("Failed to read certificate into memory: %s\n"),
304                              strerror(err));
305                 close(fd);
306                 gnutls_free(datum->data);
307                 return -EIO;
308         }
309         close(fd);
310         return 0;
311 }
312
313 /* Pull in our local copy of GnuTLS's parse_pkcs12() function, for now */
314 #include "gnutls_pkcs12.c"
315
316 /* A non-zero, non-error return to make load_certificate() continue and
317    interpreting the file as other types */
318 #define NOT_PKCS12      1
319
320 static int load_pkcs12_certificate(struct openconnect_info *vpninfo,
321                                    gnutls_datum_t *datum,
322                                    gnutls_x509_privkey_t *key,
323                                    gnutls_x509_crt_t *cert,
324                                    gnutls_x509_crt_t **extra_certs,
325                                    unsigned int *nr_extra_certs,
326                                    gnutls_x509_crl_t *crl)
327 {
328         gnutls_pkcs12_t p12;
329         char *pass;
330         int err;
331
332         err = gnutls_pkcs12_init(&p12);
333         if (err) {
334                 vpn_progress(vpninfo, PRG_ERR,
335                              _("Failed to setup PKCS#12 data structure: %s\n"),
336                              gnutls_strerror(err));
337                 return -EIO;
338         }
339
340         err = gnutls_pkcs12_import(p12, datum, GNUTLS_X509_FMT_DER, 0);
341         if (err) {
342                 gnutls_pkcs12_deinit(p12);
343                 if (vpninfo->cert_type == CERT_TYPE_UNKNOWN)
344                         return NOT_PKCS12;
345                 vpn_progress(vpninfo, PRG_ERR,
346                              _("Failed to import PKCS#12 file: %s\n"),
347                              gnutls_strerror(err));
348                 return -EINVAL;
349         }
350
351         pass = vpninfo->cert_password;
352         while ((err = gnutls_pkcs12_verify_mac(p12, pass)) == GNUTLS_E_MAC_VERIFY_FAILED) {
353                 if (pass)
354                         vpn_progress(vpninfo, PRG_ERR,
355                                      _("Failed to decrypt PKCS#12 certificate file\n"));
356                 free(pass);
357                 vpninfo->cert_password = NULL;
358                 err = request_passphrase(vpninfo, &pass,
359                                          _("Enter PKCS#12 pass phrase:"));
360                 if (err) {
361                         gnutls_pkcs12_deinit(p12);
362                         return -EINVAL;
363                 }
364         }
365         /* If it wasn't GNUTLS_E_MAC_VERIFY_FAILED, then the problem wasn't just a
366            bad password. Give up. */
367         if (err) {
368                 int level = PRG_ERR;
369                 int ret = -EINVAL;
370
371                 gnutls_pkcs12_deinit(p12);
372
373                 /* If the first attempt, and we didn't know for sure it was PKCS#12
374                    anyway, bail out and try loading it as something different. */
375                 if (pass == vpninfo->cert_password &&
376                     vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
377                         /* Make it non-fatal... */
378                         level = PRG_TRACE;
379                         ret = NOT_PKCS12;
380                 }
381
382                 vpn_progress(vpninfo, level,
383                              _("Failed to process PKCS#12 file: %s\n"),
384                                gnutls_strerror(err));
385                 return ret;
386         }
387
388         err = parse_pkcs12(vpninfo->https_cred, p12, pass, key, cert,
389                            extra_certs, nr_extra_certs, crl);
390         gnutls_pkcs12_deinit(p12);
391         if (err) {
392                 vpn_progress(vpninfo, PRG_ERR,
393                              _("Failed to load PKCS#12 certificate: %s\n"),
394                              gnutls_strerror(err));
395                 return -EINVAL;
396         }
397         return 0;
398 }
399
400 /* Older versions of GnuTLS didn't actually bother to check this, so we'll
401    do it for them. */
402 static int check_issuer_sanity(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer)
403 {
404 #if GNUTLS_VERSION_NUMBER > 0x300014
405         return 0;
406 #else
407         unsigned char id1[512], id2[512];
408         size_t id1_size = 512, id2_size = 512;
409         int err;
410
411         err = gnutls_x509_crt_get_authority_key_id(cert, id1, &id1_size, NULL);
412         if (err)
413                 return 0;
414
415         err = gnutls_x509_crt_get_subject_key_id(issuer, id2, &id2_size, NULL);
416         if (err)
417                 return 0;
418         if (id1_size == id2_size && !memcmp(id1, id2, id1_size))
419                 return 0;
420
421         /* EEP! */
422         return -EIO;
423 #endif
424 }
425
426 static int load_certificate(struct openconnect_info *vpninfo)
427 {
428         gnutls_datum_t fdata;
429         gnutls_x509_privkey_t key = NULL;
430         gnutls_x509_crl_t crl = NULL;
431         gnutls_x509_crt_t last_cert, cert = NULL;
432         gnutls_x509_crt_t *extra_certs = NULL, *supporting_certs = NULL;
433         unsigned int nr_supporting_certs, nr_extra_certs = 0;
434         int err; /* GnuTLS error */
435         int ret = 0; /* our error (zero or -errno) */
436         int i;
437
438         if (vpninfo->cert_type == CERT_TYPE_TPM) {
439                 vpn_progress(vpninfo, PRG_ERR,
440                              _("TPM support not available with GnuTLS\n"));
441                 return -EINVAL;
442         }
443
444         if (!strncmp(vpninfo->cert, "pkcs11:", 7)) {
445                 vpn_progress(vpninfo, PRG_TRACE,
446                              _("Using PKCS#11 certificate %s\n"), vpninfo->cert);
447
448                 err = gnutls_certificate_set_x509_key_file(vpninfo->https_cred,
449                                                            vpninfo->cert,
450                                                            vpninfo->sslkey,
451                                                            GNUTLS_X509_FMT_PEM);
452                 if (err) {
453                         vpn_progress(vpninfo, PRG_ERR,
454                                      _("Error loading PKCS#11 certificate: %s\n"),
455                                      gnutls_strerror(err));
456                         return -EIO;
457                 }
458                 return 0;
459         }
460
461         vpn_progress(vpninfo, PRG_TRACE,
462                      _("Using certificate file %s\n"), vpninfo->cert);
463
464         ret = load_datum(vpninfo, &fdata, vpninfo->cert);
465         if (ret)
466                 return ret;
467
468         if (vpninfo->cert_type == CERT_TYPE_PKCS12 ||
469             vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
470                 ret = load_pkcs12_certificate(vpninfo, &fdata, &key, &cert,
471                                               &extra_certs, &nr_extra_certs, &crl);
472                 if (ret < 0) {
473                         gnutls_free(fdata.data);
474                         return ret;
475                 } else if (!ret)
476                         goto got_cert;
477
478                 /* It returned NOT_PKCS12.
479                    Fall through to try PEM formats. */
480         }
481
482         gnutls_x509_crt_init(&cert);
483         err = gnutls_x509_crt_import(cert, &fdata, GNUTLS_X509_FMT_PEM);
484         if (err) {
485                 vpn_progress(vpninfo, PRG_ERR,
486                              _("Loading certificate failed: %s\n"),
487                              gnutls_strerror(err));
488                 gnutls_free(fdata.data);
489                 return -EINVAL;
490         }
491
492         if (vpninfo->sslkey != vpninfo->cert) {
493                 gnutls_free(fdata.data);
494
495                 vpn_progress(vpninfo, PRG_TRACE,
496                              _("Using private key file %s\n"), vpninfo->cert);
497
498                 ret = load_datum(vpninfo, &fdata, vpninfo->sslkey);
499                 if (ret)
500                         goto out;
501         }
502
503         gnutls_x509_privkey_init(&key);
504         /* Try PKCS#1 (and PKCS#8 without password) first. GnuTLS doesn't
505            support OpenSSL's old PKCS#1-based encrypted format. We should
506            probably check for it and give a more coherent failure mode. */
507         err = gnutls_x509_privkey_import(key, &fdata, GNUTLS_X509_FMT_PEM);
508         if (err) {
509                 /* If that fails, try PKCS#8 */
510                 char *pass = vpninfo->cert_password;
511
512                 /* Yay, just for fun this is *different* to PKCS#12. Where we could
513                    try an empty password there, in this case the empty-password case
514                    has already been *tried* by gnutls_x509_privkey_import(). If we
515                    just call gnutls_x509_privkey_import_pkcs8() with a NULL password,
516                    it'll SEGV. You have to set the GNUTLS_PKCS_PLAIN flag if you want
517                    to try without a password. Passing NULL evidently isn't enough of
518                    a hint. */
519                 while ((err = gnutls_x509_privkey_import_pkcs8(key, &fdata,
520                                                                GNUTLS_X509_FMT_PEM,
521                                                                pass?pass:"", 0))) {
522                         if (err != GNUTLS_E_DECRYPTION_FAILED) {
523                                 vpn_progress(vpninfo, PRG_ERR,
524                                              _("Failed to load private key as PKCS#8: %s\n"),
525                                              gnutls_strerror(err));
526                                 ret = -EINVAL;
527                                 goto out;
528                         }
529                         if (pass) {
530                                 vpn_progress(vpninfo, PRG_ERR,
531                                              _("Failed to decrypt PKCS#8 certificate file\n"));
532                                 free (pass);
533                         }
534                         err = request_passphrase(vpninfo, &pass,
535                                                  _("Enter PEM pass phrase:"));
536                         if (err) {
537                                 ret = -EINVAL;
538                                 goto out;
539                         }
540                 }
541         }
542  got_cert:
543         check_certificate_expiry(vpninfo, cert);
544
545         if (crl) {
546                 err = gnutls_certificate_set_x509_crl(vpninfo->https_cred, &crl, 1);
547                 if (err) {
548                         vpn_progress(vpninfo, PRG_ERR,
549                                      _("Setting certificate recovation list failed: %s\n"),
550                                      gnutls_strerror(err));
551                         goto out;
552                 }
553         }
554
555         /* OpenSSL has problems with certificate chains — if there are
556            multiple certs with the same name, it doesn't necessarily
557            choose the _right_ one. (RT#1942)
558            Pick the right ones for ourselves and add them manually. */
559         last_cert = cert;
560         nr_supporting_certs = 1; /* Our starting cert */
561         while (1) {
562                 gnutls_x509_crt_t issuer;
563                 char name[80];
564                 size_t namelen;
565
566                 for (i = 0; i < nr_extra_certs; i++) {
567                         if (gnutls_x509_crt_check_issuer(last_cert, extra_certs[i]) &&
568                             !check_issuer_sanity(last_cert, extra_certs[i]))
569                                 break;
570                 }
571
572                 if (i < nr_extra_certs) {
573                         issuer = extra_certs[i];
574                 } else {
575                         err = gnutls_certificate_get_issuer(vpninfo->https_cred,
576                                                             last_cert, &issuer, 0);
577                         if (err) {
578                                 printf("can't get issuer for %p: %s\n",
579                                        last_cert, gnutls_strerror(err));
580                                 break;
581                         }
582                 }
583
584                 /* The check_issuer_sanity() function works fine as a workaround where
585                    it was used above, but when gnutls_certificate_get_issuer() returns
586                    a bogus cert, there's nothing we can do to fix it up. We don't get
587                    to iterate over all the available certs like we can over our own
588                    list. */
589                 if (check_issuer_sanity(last_cert, issuer)) {
590                         /* Hm, is there a bug reference for this? Or just the git commit
591                            reference (c1ef7efb in master, 5196786c in gnutls_3_0_x-2)? */
592                         vpn_progress(vpninfo, PRG_ERR,
593                                      _("WARNING: GnuTLS returned incorrect issuer certs; authentication may fail!\n"));
594                         break;
595                 }
596
597                 if (issuer == last_cert)
598                         break;
599
600                 /* OK, we found a new cert to add to our chain. */
601                 supporting_certs = realloc(supporting_certs,
602                                            sizeof(cert) * ++nr_supporting_certs);
603                 if (!supporting_certs) {
604                         vpn_progress(vpninfo, PRG_ERR,
605                                      _("Failed to allocate memory for supporting certificates\n"));
606                         /* The world is probably about to end, but try without them anyway */
607                         break;
608                 }
609
610                 /* First time we actually allocated an array? Copy the first cert into it */
611                 if (nr_supporting_certs == 2)
612                         supporting_certs[0] = cert;
613
614                 /* Append the new one */
615                 supporting_certs[nr_supporting_certs-1] = issuer;
616                 last_cert = issuer;
617
618                 /* Logging. */
619                 sprintf(name, "<unknown>");
620                 namelen = sizeof(name);
621                 if (gnutls_x509_crt_get_dn_by_oid(issuer, GNUTLS_OID_X520_COMMON_NAME, 0, 0,
622                                                   name, &namelen) &&
623                     gnutls_x509_crt_get_dn(issuer, name, &namelen))
624                         sprintf(name, "<unknown>");
625
626                 vpn_progress(vpninfo, PRG_DEBUG,
627                              _("Adding supporting CA '%s'\n"), name);
628         }
629
630         err = gnutls_certificate_set_x509_key(vpninfo->https_cred,
631                                               supporting_certs ? supporting_certs : &cert,
632                                               supporting_certs ? 1 : nr_supporting_certs,
633                                               key);
634         if (err) {
635                 vpn_progress(vpninfo, PRG_ERR,
636                              _("Setting certificate failed: %s\n"),
637                              gnutls_strerror(err));
638                 ret = -EIO;
639         }
640  out:
641         if (crl)
642                 gnutls_x509_crl_deinit(crl);
643         if (key)
644                 gnutls_x509_privkey_deinit(key);
645         if (cert)
646                 gnutls_x509_crt_deinit(cert);
647         for (i = 0; i < nr_extra_certs; i++)
648                 gnutls_x509_crt_deinit(extra_certs[i]);
649         free(extra_certs);
650         free(supporting_certs);
651         gnutls_free(fdata.data);
652         return ret;
653 }
654
655 static int get_cert_fingerprint(struct openconnect_info *vpninfo,
656                                 gnutls_x509_crt_t cert,
657                                 gnutls_digest_algorithm_t algo,
658                                 char *buf)
659 {
660         unsigned char md[256];
661         size_t md_size = sizeof(md);
662         unsigned int i;
663
664         if (gnutls_x509_crt_get_fingerprint(cert, algo, md, &md_size))
665                 return -EIO;
666
667         for (i=0; i < md_size; i++)
668                 sprintf(&buf[i*2], "%02X", md[i]);
669
670         return 0;
671 }
672
673 int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
674                              OPENCONNECT_X509 *cert, char *buf)
675 {
676         return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_MD5, buf);
677 }
678
679 int openconnect_get_cert_sha1(struct openconnect_info *vpninfo,
680                               OPENCONNECT_X509 *cert, char *buf)
681 {
682         return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_SHA1, buf);
683 }
684
685 char *openconnect_get_cert_details(struct openconnect_info *vpninfo,
686                                    OPENCONNECT_X509 *cert)
687 {
688         gnutls_datum_t buf;
689         char *ret;
690
691         if (gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &buf))
692                 return NULL;
693         
694         /* Just in case gnutls_free() isn't free(), we can't steal it. */
695         ret = strdup((char *)buf.data);
696         gnutls_free(buf.data);
697         
698         return ret;
699 }
700
701 int openconnect_get_cert_DER(struct openconnect_info *vpninfo,
702                              OPENCONNECT_X509 *cert, unsigned char **buf)
703 {
704         size_t l = 0;
705         unsigned char *ret = NULL;
706
707         if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l) != 
708             GNUTLS_E_SHORT_MEMORY_BUFFER)
709                 return -EIO;
710
711         ret = malloc(l);
712         if (!ret)
713                 return -ENOMEM;
714
715         if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l)) {
716                 free(ret);
717                 return -EIO;
718         }
719         *buf = ret;
720         return l;
721 }
722
723 static int verify_peer(gnutls_session_t session)
724 {
725         struct openconnect_info *vpninfo = gnutls_session_get_ptr(session);
726         const gnutls_datum_t *cert_list;
727         gnutls_x509_crt_t cert;
728         unsigned int status, cert_list_size;
729         char *reason = NULL;
730         int err;
731
732         cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
733         if (!cert_list) {
734                 vpn_progress(vpninfo, PRG_ERR, _("Server presented no certificate\n"));
735                 return GNUTLS_E_CERTIFICATE_ERROR;
736         }
737
738         if (vpninfo->servercert) {
739                 unsigned char sha1bin[SHA1_SIZE];
740                 char fingerprint[(SHA1_SIZE * 2) + 1];
741                 int i;
742                 
743                 err = openconnect_sha1(sha1bin, cert_list[0].data, cert_list[0].size);
744                 if (err) {
745                         vpn_progress(vpninfo, PRG_ERR,
746                                      _("Could not calculate SHA1 of server's certificate\n"));
747                         return GNUTLS_E_CERTIFICATE_ERROR;
748                 }
749                 for (i=0; i < SHA1_SIZE; i++)
750                         sprintf(&fingerprint[i*2], "%02X", sha1bin[i]);
751
752                 if (strcasecmp(vpninfo->servercert, fingerprint)) {
753                         vpn_progress(vpninfo, PRG_ERR,
754                                      _("Server SSL certificate didn't match: %s\n"), fingerprint);
755                         return GNUTLS_E_CERTIFICATE_ERROR;
756                 }
757                 return 0;
758         }
759
760         err = gnutls_certificate_verify_peers2 (session, &status);
761         if (err) {
762                 vpn_progress(vpninfo, PRG_ERR, _("Error checking server cert status\n"));
763                 return GNUTLS_E_CERTIFICATE_ERROR;
764         }
765
766         if (status & GNUTLS_CERT_REVOKED)
767                 reason = _("certificate revoked");
768         else if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
769                 reason = _("signer not found");
770         else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
771                 reason = _("signer not a CA certificate");
772         else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
773                 reason = _("insecure algorithm");
774         else if (status & GNUTLS_CERT_NOT_ACTIVATED)
775                 reason = _("certificate not yet activated");
776         else if (status & GNUTLS_CERT_EXPIRED)
777                 reason = _("certificate expired");
778         else if (status & GNUTLS_CERT_INVALID)
779                 /* If this is set and no other reason, it apparently means
780                    that signature verification failed. Not entirely sure
781                    why we don't just set a bit for that too. */
782                 reason = _("signature verification failed");
783
784         err = gnutls_x509_crt_init(&cert);
785         if (err) {
786                 vpn_progress(vpninfo, PRG_ERR, _("Error initialising X509 cert structure\n"));
787                 return GNUTLS_E_CERTIFICATE_ERROR;
788         }
789
790         err = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
791         if (err) {
792                 vpn_progress(vpninfo, PRG_ERR, _("Error importing server's cert\n"));
793                 gnutls_x509_crt_deinit(cert);
794                 return GNUTLS_E_CERTIFICATE_ERROR;
795         }
796
797         if (!reason && !gnutls_x509_crt_check_hostname(cert, vpninfo->hostname))
798                 reason = _("certificate does not match hostname");
799
800         if (reason) {
801                 vpn_progress(vpninfo, PRG_ERR, "Server certificate verify failed: %s\n",
802                              reason);
803                 if (vpninfo->validate_peer_cert)
804                         err = vpninfo->validate_peer_cert(vpninfo->cbdata,
805                                                           cert,
806                                                           reason) ? GNUTLS_E_CERTIFICATE_ERROR : 0;
807                 else
808                         err = GNUTLS_E_CERTIFICATE_ERROR;
809         }
810
811         gnutls_x509_crt_deinit(cert);
812         return err;
813 }
814
815 static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo)
816 {
817         /* OpenSSL has problems with certificate chains -- if there are
818            multiple certs with the same name, it doesn't necessarily
819            choose the _right_ one. (RT#1942)
820            Pick the right ones for ourselves and add them manually. */
821         
822         /* FIXME: Of course we still have to do this with GnuTLS, to work
823            around the issue on the server side */
824 }
825
826 static int cancellable_connect(struct openconnect_info *vpninfo, int sockfd,
827                                const struct sockaddr *addr, socklen_t addrlen)
828 {
829         struct sockaddr_storage peer;
830         socklen_t peerlen = sizeof(peer);
831         fd_set wr_set, rd_set;
832         int maxfd = sockfd;
833
834         fcntl(sockfd, F_SETFL, fcntl(sockfd, F_GETFL) | O_NONBLOCK);
835
836         if (connect(sockfd, addr, addrlen) < 0 && errno != EINPROGRESS)
837                 return -1;
838
839         FD_ZERO(&wr_set);
840         FD_ZERO(&rd_set);
841         FD_SET(sockfd, &wr_set);
842         if (vpninfo->cancel_fd != -1) {
843                 FD_SET(vpninfo->cancel_fd, &rd_set);
844                 if (vpninfo->cancel_fd > sockfd)
845                         maxfd = vpninfo->cancel_fd;
846         }
847         
848         /* Later we'll render this whole exercise non-pointless by
849            including a 'cancelfd' here too. */
850         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
851         if (vpninfo->cancel_fd != -1 && FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
852                 vpn_progress(vpninfo, PRG_ERR, _("Socket connect cancelled\n"));
853                 errno = EINTR;
854                 return -1;
855         }
856                 
857         /* Check whether connect() succeeded or failed by using
858            getpeername(). See http://cr.yp.to/docs/connect.html */
859         return getpeername(sockfd, (void *)&peer, &peerlen);
860 }
861
862 int openconnect_open_https(struct openconnect_info *vpninfo)
863 {
864         int ssl_sock = -1;
865         int err;
866
867         if (vpninfo->https_sess)
868                 return 0;
869
870         if (!vpninfo->port)
871                 vpninfo->port = 443;
872
873         if (vpninfo->peer_addr) {
874 #ifdef SOCK_CLOEXEC
875                 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM | SOCK_CLOEXEC, IPPROTO_IP);
876                 if (ssl_sock < 0)
877 #endif
878                 {
879                         ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
880                         if (ssl_sock < 0)
881                                 goto reconn_err;
882                         fcntl(ssl_sock, F_SETFD, fcntl(ssl_sock, F_GETFD) | FD_CLOEXEC);
883                 }
884                 if (cancellable_connect(vpninfo, ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen)) {
885                 reconn_err:
886                         if (vpninfo->proxy) {
887                                 vpn_progress(vpninfo, PRG_ERR, 
888                                              _("Failed to reconnect to proxy %s\n"),
889                                              vpninfo->proxy);
890                         } else {
891                                 vpn_progress(vpninfo, PRG_ERR, 
892                                              _("Failed to reconnect to host %s\n"),
893                                              vpninfo->hostname);
894                         }
895                         return -EINVAL;
896                 }
897                 
898         } else {
899                 struct addrinfo hints, *result, *rp;
900                 char *hostname;
901                 char port[6];
902
903                 memset(&hints, 0, sizeof(struct addrinfo));
904                 hints.ai_family = AF_UNSPEC;
905                 hints.ai_socktype = SOCK_STREAM;
906                 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
907                 hints.ai_protocol = 0;
908                 hints.ai_canonname = NULL;
909                 hints.ai_addr = NULL;
910                 hints.ai_next = NULL;
911
912                 /* The 'port' variable is a string because it's easier
913                    this way than if we pass NULL to getaddrinfo() and
914                    then try to fill in the numeric value into
915                    different types of returned sockaddr_in{6,}. */
916 #ifdef LIBPROXY_HDR
917                 if (vpninfo->proxy_factory) {
918                         char *url;
919                         char **proxies;
920                         int i = 0;
921
922                         free(vpninfo->proxy_type);
923                         vpninfo->proxy_type = NULL;
924                         free(vpninfo->proxy);
925                         vpninfo->proxy = NULL;
926
927                         if (vpninfo->port == 443)
928                                 i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
929                                              vpninfo->urlpath?:"");
930                         else
931                                 i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
932                                              vpninfo->port, vpninfo->urlpath?:"");
933                         if (i == -1)
934                                 return -ENOMEM;
935
936                         proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
937                                                                url);
938
939                         i = 0;
940                         while (proxies && proxies[i]) {
941                                 if (!vpninfo->proxy &&
942                                     (!strncmp(proxies[i], "http://", 7) ||
943                                      !strncmp(proxies[i], "socks://", 8) ||
944                                      !strncmp(proxies[i], "socks5://", 9)))
945                                         internal_parse_url(proxies[i], &vpninfo->proxy_type,
946                                                   &vpninfo->proxy, &vpninfo->proxy_port,
947                                                   NULL, 0);
948                                 i++;
949                         }
950                         free(url);
951                         free(proxies);
952                         if (vpninfo->proxy)
953                                 vpn_progress(vpninfo, PRG_TRACE,
954                                              _("Proxy from libproxy: %s://%s:%d/\n"),
955                                              vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
956                 }
957 #endif
958                 if (vpninfo->proxy) {
959                         hostname = vpninfo->proxy;
960                         snprintf(port, 6, "%d", vpninfo->proxy_port);
961                 } else {
962                         hostname = vpninfo->hostname;
963                         snprintf(port, 6, "%d", vpninfo->port);
964                 }
965
966                 if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
967                         /* Solaris has no strndup(). */
968                         int len = strlen(hostname) - 2;
969                         char *new_hostname = malloc(len + 1);
970                         if (!new_hostname)
971                                 return -ENOMEM;
972                         memcpy(new_hostname, hostname + 1, len);
973                         new_hostname[len] = 0;
974
975                         hostname = new_hostname;
976                         hints.ai_flags |= AI_NUMERICHOST;
977                 }
978
979                 err = getaddrinfo(hostname, port, &hints, &result);
980                 if (hints.ai_flags & AI_NUMERICHOST)
981                         free(hostname);
982
983                 if (err) {
984                         vpn_progress(vpninfo, PRG_ERR,
985                                      _("getaddrinfo failed for host '%s': %s\n"),
986                                      hostname, gai_strerror(err));
987                         return -EINVAL;
988                 }
989
990                 for (rp = result; rp ; rp = rp->ai_next) {
991                         char host[80];
992
993                         if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
994                                          sizeof(host), NULL, 0, NI_NUMERICHOST))
995                                 vpn_progress(vpninfo, PRG_INFO,
996                                              _("Attempting to connect to %s%s%s:%s\n"),
997                                              rp->ai_family == AF_INET6?"[":"",
998                                              host,
999                                              rp->ai_family == AF_INET6?"]":"",
1000                                              port);
1001                         
1002                         ssl_sock = socket(rp->ai_family, rp->ai_socktype,
1003                                           rp->ai_protocol);
1004                         if (ssl_sock < 0)
1005                                 continue;
1006                         if (cancellable_connect(vpninfo, ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
1007                                 /* Store the peer address we actually used, so that DTLS can
1008                                    use it again later */
1009                                 vpninfo->peer_addr = malloc(rp->ai_addrlen);
1010                                 if (!vpninfo->peer_addr) {
1011                                         vpn_progress(vpninfo, PRG_ERR,
1012                                                      _("Failed to allocate sockaddr storage\n"));
1013                                         close(ssl_sock);
1014                                         return -ENOMEM;
1015                                 }
1016                                 vpninfo->peer_addrlen = rp->ai_addrlen;
1017                                 memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
1018                                 break;
1019                         }
1020                         close(ssl_sock);
1021                         ssl_sock = -1;
1022                 }
1023                 freeaddrinfo(result);
1024                 
1025                 if (ssl_sock < 0) {
1026                         vpn_progress(vpninfo, PRG_ERR,
1027                                      _("Failed to connect to host %s\n"),
1028                                      vpninfo->proxy?:vpninfo->hostname);
1029                         return -EINVAL;
1030                 }
1031         }
1032
1033         if (vpninfo->proxy) {
1034                 err = process_proxy(vpninfo, ssl_sock);
1035                 if (err) {
1036                         close(ssl_sock);
1037                         return err;
1038                 }
1039         }
1040
1041         if (!vpninfo->https_cred) {
1042                 gnutls_certificate_allocate_credentials(&vpninfo->https_cred);
1043                 gnutls_certificate_set_x509_trust_file(vpninfo->https_cred,
1044                                                        "/etc/pki/tls/certs/ca-bundle.crt",
1045                                                        GNUTLS_X509_FMT_PEM);
1046                 gnutls_certificate_set_verify_function (vpninfo->https_cred,
1047                                                         verify_peer);
1048                 /* FIXME: Ensure TLSv1.0, no options */
1049
1050                 if (vpninfo->cert) {
1051                         err = load_certificate(vpninfo);
1052                         if (err) {
1053                                 vpn_progress(vpninfo, PRG_ERR,
1054                                              _("Loading certificate failed. Aborting.\n"));
1055                                 return err;
1056                         }
1057                 }
1058
1059                 /* We just want to do:
1060                    SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY); 
1061                    ... but it doesn't work with OpenSSL < 0.9.8k because of 
1062                    problems with inheritance (fixed in v1.1.4.6 of
1063                    crypto/x509/x509_vpm.c) so we have to play silly buggers
1064                    instead. This trick doesn't work _either_ in < 0.9.7 but
1065                    I don't know of _any_ workaround which will, and can't
1066                    be bothered to find out either. */
1067
1068                 if (vpninfo->cafile) {
1069                         err = gnutls_certificate_set_x509_trust_file(vpninfo->https_cred,
1070                                                                      vpninfo->cafile,
1071                                                                      GNUTLS_X509_FMT_PEM);
1072                         if (err < 0) {
1073                                 vpn_progress(vpninfo, PRG_ERR,
1074                                              _("Failed to open CA file '%s': %s\n"),
1075                                              vpninfo->cafile, gnutls_strerror(err));
1076                                 close(ssl_sock);
1077                                 return -EINVAL;
1078                         }
1079                 }
1080
1081         }
1082         gnutls_init (&vpninfo->https_sess, GNUTLS_CLIENT);
1083         gnutls_session_set_ptr (vpninfo->https_sess, (void *) vpninfo);
1084         err = gnutls_priority_set_direct (vpninfo->https_sess, "NONE:+VERS-TLS1.0:+SHA1:+AES-128-CBC:+RSA:+COMP-NULL:%COMPAT:%DISABLE_SAFE_RENEGOTIATION", NULL);
1085         if (err) {
1086                 vpn_progress(vpninfo, PRG_ERR,
1087                              _("Failed to set TLS priority string: %s\n"),
1088                              gnutls_strerror(err));
1089                 return -EIO;
1090         }
1091
1092         gnutls_record_disable_padding (vpninfo->https_sess);
1093         workaround_openssl_certchain_bug(vpninfo);
1094         gnutls_credentials_set (vpninfo->https_sess, GNUTLS_CRD_CERTIFICATE, vpninfo->https_cred);
1095         gnutls_transport_set_ptr(vpninfo->https_sess, /* really? */(gnutls_transport_ptr_t)(long) ssl_sock);
1096
1097         vpn_progress(vpninfo, PRG_INFO, _("SSL negotiation with %s\n"),
1098                      vpninfo->hostname);
1099
1100         while ((err = gnutls_handshake (vpninfo->https_sess))) {
1101                 if (err == GNUTLS_E_AGAIN) {
1102                         fd_set rd_set, wr_set;
1103                         int maxfd = ssl_sock;
1104                         
1105                         FD_ZERO(&rd_set);
1106                         FD_ZERO(&wr_set);
1107                         
1108                         if (gnutls_record_get_direction(vpninfo->https_sess))
1109                                 FD_SET(ssl_sock, &wr_set);
1110                         else
1111                                 FD_SET(ssl_sock, &rd_set);
1112
1113                         if (vpninfo->cancel_fd != -1) {
1114                                 FD_SET(vpninfo->cancel_fd, &rd_set);
1115                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
1116                                         maxfd = vpninfo->cancel_fd;
1117                         }
1118                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
1119                         if (vpninfo->cancel_fd != -1 &&
1120                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
1121                                 vpn_progress(vpninfo, PRG_ERR, _("SSL connection cancelled\n"));
1122                                 gnutls_deinit(vpninfo->https_sess);
1123                                 vpninfo->https_sess = NULL;
1124                                 close(ssl_sock);
1125                                 return -EINTR;
1126                         }
1127                 } else if (err == GNUTLS_E_INTERRUPTED || gnutls_error_is_fatal(err)) {
1128                         vpn_progress(vpninfo, PRG_ERR, _("SSL connection failure: %s\n"),
1129                                                          gnutls_strerror(err));
1130                         gnutls_deinit(vpninfo->https_sess);
1131                         vpninfo->https_sess = NULL;
1132                         close(ssl_sock);
1133                         return -EIO;
1134                 } else {
1135                         /* non-fatal error or warning. Ignore it and continue */
1136                         vpn_progress(vpninfo, PRG_TRACE,
1137                                      _("GnuTLS non-fatal return during handshake: %s\n"),
1138                                      gnutls_strerror(err));
1139                 }
1140         }
1141
1142         vpninfo->ssl_fd = ssl_sock;
1143
1144         vpn_progress(vpninfo, PRG_INFO, _("Connected to HTTPS on %s\n"),
1145                      vpninfo->hostname);
1146
1147         return 0;
1148 }
1149
1150 void openconnect_close_https(struct openconnect_info *vpninfo)
1151 {
1152 #if 0
1153         if (vpninfo->peer_cert) {
1154                 X509_free(vpninfo->peer_cert);
1155                 vpninfo->peer_cert = NULL;
1156         }
1157 #endif
1158         if (vpninfo->https_sess) {
1159                 gnutls_deinit(vpninfo->https_sess);
1160                 vpninfo->https_sess = NULL;
1161         }
1162         if (vpninfo->ssl_fd != -1) {
1163                 close(vpninfo->ssl_fd);
1164                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_rfds);
1165                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
1166                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_efds);
1167                 vpninfo->ssl_fd = -1;
1168         }
1169 }
1170
1171 void openconnect_init_openssl(void)
1172 {
1173         gnutls_global_init();
1174 }
1175
1176 int openconnect_sha1(unsigned char *result, void *data, int datalen)
1177 {
1178         gnutls_datum_t d;
1179         size_t shalen = SHA1_SIZE;
1180
1181         d.data = data;
1182         d.size = datalen;
1183         if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &d, result, &shalen))
1184                 return -1;
1185
1186         return 0;
1187 }
1188
1189 int openconnect_random(void *bytes, int len)
1190 {
1191         if (gnutls_rnd(GNUTLS_RND_RANDOM, bytes, len))
1192                 return -EIO;
1193         return 0;
1194 }
1195
1196 int openconnect_local_cert_md5(struct openconnect_info *vpninfo,
1197                                char *buf)
1198 {
1199         const gnutls_datum_t *d;
1200         size_t md5len = 16;
1201
1202         buf[0] = 0;
1203
1204         d = gnutls_certificate_get_ours(vpninfo->https_sess);
1205         if (!d)
1206                 return -EIO;
1207
1208         if (gnutls_fingerprint(GNUTLS_DIG_MD5, d, buf, &md5len))
1209                 return -EIO;
1210
1211         return 0;
1212 }
1213