Check for gnutls_pkcs12_simple_parse() in 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 /* Helper functions for reading/writing lines over SSL.
48    We could use cURL for the HTTP stuff, but it's overkill */
49
50 int openconnect_SSL_write(struct openconnect_info *vpninfo, char *buf, size_t len)
51 {
52         size_t orig_len = len;
53
54         while (len) {
55                 int done = gnutls_record_send(vpninfo->https_sess, buf, len);
56                 if (done > 0)
57                         len -= done;
58                 else if (done != GNUTLS_E_AGAIN) {
59                         vpn_progress(vpninfo, PRG_ERR, _("Failed to write to SSL socket: %s"),
60                                      gnutls_strerror(done));
61                         return -EIO;
62                 } else {
63                         fd_set wr_set, rd_set;
64                         int maxfd = vpninfo->ssl_fd;
65
66                         FD_ZERO(&wr_set);
67                         FD_ZERO(&rd_set);
68                         
69                         if (gnutls_record_get_direction(vpninfo->https_sess))
70                                 FD_SET(vpninfo->ssl_fd, &wr_set);
71                         else
72                                 FD_SET(vpninfo->ssl_fd, &rd_set);
73
74                         if (vpninfo->cancel_fd != -1) {
75                                 FD_SET(vpninfo->cancel_fd, &rd_set);
76                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
77                                         maxfd = vpninfo->cancel_fd;
78                         }
79                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
80                         if (vpninfo->cancel_fd != -1 &&
81                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
82                                 vpn_progress(vpninfo, PRG_ERR, _("SSL write cancelled\n"));
83                                 return -EINTR;
84                         }
85                 }
86         }
87         return orig_len;
88 }
89
90 int openconnect_SSL_read(struct openconnect_info *vpninfo, char *buf, size_t len)
91 {
92         int done;
93
94         while ((done = gnutls_record_recv(vpninfo->https_sess, buf, len)) < 0) {
95                 fd_set wr_set, rd_set;
96                 int maxfd = vpninfo->ssl_fd;
97
98                 if (done != GNUTLS_E_AGAIN) {
99                         vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket: %s"),
100                                      gnutls_strerror(done));
101                         return -EIO;
102                 } else {
103                         FD_ZERO(&wr_set);
104                         FD_ZERO(&rd_set);
105                         
106                         if (gnutls_record_get_direction(vpninfo->https_sess))
107                                 FD_SET(vpninfo->ssl_fd, &wr_set);
108                         else
109                                 FD_SET(vpninfo->ssl_fd, &rd_set);
110
111                         if (vpninfo->cancel_fd != -1) {
112                                 FD_SET(vpninfo->cancel_fd, &rd_set);
113                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
114                                         maxfd = vpninfo->cancel_fd;
115                         }
116                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
117                         if (vpninfo->cancel_fd != -1 &&
118                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
119                                 vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
120                                 return -EINTR;
121                         }
122                 }
123         }
124         return done;
125 }
126
127 int openconnect_SSL_gets(struct openconnect_info *vpninfo, char *buf, size_t len)
128 {
129         int i = 0;
130         int ret;
131
132         if (len < 2)
133                 return -EINVAL;
134
135         while (1) {
136                 ret = gnutls_record_recv(vpninfo->https_sess, buf + i, 1);
137                 if (ret == 1) {
138                         if (buf[i] == '\n') {
139                                 buf[i] = 0;
140                                 if (i && buf[i-1] == '\r') {
141                                         buf[i-1] = 0;
142                                         i--;
143                                 }
144                                 return i;
145                         }
146                         i++;
147
148                         if (i >= len - 1) {
149                                 buf[i] = 0;
150                                 return i;
151                         }
152                 } else if (ret != GNUTLS_E_AGAIN) {
153                         vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket: %s\n"),
154                                      gnutls_strerror(ret));
155                         ret = -EIO;
156                         break;
157                 } else {
158                         fd_set rd_set, wr_set;
159                         int maxfd = vpninfo->ssl_fd;
160                         
161                         FD_ZERO(&rd_set);
162                         FD_ZERO(&wr_set);
163                         
164                         if (gnutls_record_get_direction(vpninfo->https_sess))
165                                 FD_SET(vpninfo->ssl_fd, &wr_set);
166                         else
167                                 FD_SET(vpninfo->ssl_fd, &rd_set);
168
169                         if (vpninfo->cancel_fd != -1) {
170                                 FD_SET(vpninfo->cancel_fd, &rd_set);
171                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
172                                         maxfd = vpninfo->cancel_fd;
173                         }
174                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
175                         if (vpninfo->cancel_fd != -1 &&
176                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
177                                 vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
178                                 ret = -EINTR;
179                                 break;
180                         }
181                 }
182         }
183         buf[i] = 0;
184         return i ?: ret;
185 }
186
187 static int check_certificate_expiry(struct openconnect_info *vpninfo, gnutls_x509_crt_t cert)
188 {
189         const char *reason = NULL;
190         time_t expires = gnutls_x509_crt_get_expiration_time(cert);
191         time_t now = time(NULL);
192
193         if (expires == -1) {
194                 vpn_progress(vpninfo, PRG_ERR,
195                              _("Could not extract expiration time of certificate\n"));
196                 return -EINVAL;
197         }
198
199         if (expires < now)
200                 reason = _("Client certificate has expired at");
201         else if (expires < now + vpninfo->cert_expire_warning)
202                 reason = _("Client certificate expires soon at");
203
204         if (reason) {
205                 struct tm tm;
206                 char buf[80];
207
208                 gmtime_r(&expires, &tm);
209                 strftime(buf, 80, "%a, %d %b %Y %T %Z", &tm);
210
211                 vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", reason, buf);
212         }
213         return 0;
214 }
215
216 /* For systems that don't support O_CLOEXEC, just don't bother.
217    It's not open for long anyway. */
218 #ifndef O_CLOEXEC
219 #define O_CLOEXEC
220 #endif
221
222 static int load_datum(struct openconnect_info *vpninfo,
223                       gnutls_datum_t *datum, const char *fname)
224 {
225         struct stat st;
226         int fd, err;
227
228         fd = open(fname, O_RDONLY|O_CLOEXEC);
229         if (fd == -1) {
230                 err = errno;
231                 vpn_progress(vpninfo, PRG_ERR,
232                              _("Failed to open certificate file %s: %s\n"),
233                              vpninfo->cert, strerror(err));
234                 return -ENOENT;
235         }
236         if (fstat(fd, &st)) {
237                 err = errno;
238                 vpn_progress(vpninfo, PRG_ERR,
239                              _("Failed to stat certificate file %s: %s\n"),
240                              vpninfo->cert, strerror(err));
241                 close(fd);
242                 return -EIO;
243         }
244         datum->size = st.st_size;
245         datum->data = gnutls_malloc(st.st_size + 1);
246         if (!datum->data) {
247                 vpn_progress(vpninfo, PRG_ERR,
248                              _("Failed to allocate certificate buffer\n"));
249                 close(fd);
250                 return -ENOMEM;
251         }
252         errno = EAGAIN;
253         if (read(fd, datum->data, datum->size) != datum->size) {
254                 err = errno;
255                 vpn_progress(vpninfo, PRG_ERR,
256                              _("Failed to read certificate into memory: %s\n"),
257                              strerror(err));
258                 close(fd);
259                 gnutls_free(datum->data);
260                 return -EIO;
261         }
262         datum->data[st.st_size] = 0;
263         close(fd);
264         return 0;
265 }
266
267 #ifndef HAVE_GNUTLS_PKCS12_SIMPLE_PARSE
268 /* If we're using a version of GnuTLS from before this was 
269    exported, pull in our local copy. */
270 #include "gnutls_pkcs12.c"
271 #endif
272
273 /* A non-zero, non-error return to make load_certificate() continue and
274    interpreting the file as other types */
275 #define NOT_PKCS12      1
276
277 static int load_pkcs12_certificate(struct openconnect_info *vpninfo,
278                                    gnutls_datum_t *datum,
279                                    gnutls_x509_privkey_t *key,
280                                    gnutls_x509_crt_t *cert,
281                                    gnutls_x509_crt_t **extra_certs,
282                                    unsigned int *nr_extra_certs,
283                                    gnutls_x509_crl_t *crl)
284 {
285         gnutls_pkcs12_t p12;
286         char *pass;
287         int err;
288
289         err = gnutls_pkcs12_init(&p12);
290         if (err) {
291                 vpn_progress(vpninfo, PRG_ERR,
292                              _("Failed to setup PKCS#12 data structure: %s\n"),
293                              gnutls_strerror(err));
294                 return -EIO;
295         }
296
297         err = gnutls_pkcs12_import(p12, datum, GNUTLS_X509_FMT_DER, 0);
298         if (err) {
299                 gnutls_pkcs12_deinit(p12);
300                 if (vpninfo->cert_type == CERT_TYPE_UNKNOWN)
301                         return NOT_PKCS12;
302                 vpn_progress(vpninfo, PRG_ERR,
303                              _("Failed to import PKCS#12 file: %s\n"),
304                              gnutls_strerror(err));
305                 return -EINVAL;
306         }
307
308         pass = vpninfo->cert_password;
309         while ((err = gnutls_pkcs12_verify_mac(p12, pass)) == GNUTLS_E_MAC_VERIFY_FAILED) {
310                 if (pass)
311                         vpn_progress(vpninfo, PRG_ERR,
312                                      _("Failed to decrypt PKCS#12 certificate file\n"));
313                 free(pass);
314                 vpninfo->cert_password = NULL;
315                 err = request_passphrase(vpninfo, &pass,
316                                          _("Enter PKCS#12 pass phrase:"));
317                 if (err) {
318                         gnutls_pkcs12_deinit(p12);
319                         return -EINVAL;
320                 }
321         }
322         /* If it wasn't GNUTLS_E_MAC_VERIFY_FAILED, then the problem wasn't just a
323            bad password. Give up. */
324         if (err) {
325                 int level = PRG_ERR;
326                 int ret = -EINVAL;
327
328                 gnutls_pkcs12_deinit(p12);
329
330                 /* If the first attempt, and we didn't know for sure it was PKCS#12
331                    anyway, bail out and try loading it as something different. */
332                 if (pass == vpninfo->cert_password &&
333                     vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
334                         /* Make it non-fatal... */
335                         level = PRG_TRACE;
336                         ret = NOT_PKCS12;
337                 }
338
339                 vpn_progress(vpninfo, level,
340                              _("Failed to process PKCS#12 file: %s\n"),
341                                gnutls_strerror(err));
342                 return ret;
343         }
344
345         err = gnutls_pkcs12_simple_parse(vpninfo->https_cred, p12, pass, key,
346                                          cert, extra_certs, nr_extra_certs, crl);
347         gnutls_pkcs12_deinit(p12);
348         if (err) {
349                 vpn_progress(vpninfo, PRG_ERR,
350                              _("Failed to load PKCS#12 certificate: %s\n"),
351                              gnutls_strerror(err));
352                 return -EINVAL;
353         }
354         return 0;
355 }
356
357 /* Older versions of GnuTLS didn't actually bother to check this, so we'll
358    do it for them. */
359 static int check_issuer_sanity(gnutls_x509_crt_t cert, gnutls_x509_crt_t issuer)
360 {
361 #if GNUTLS_VERSION_NUMBER > 0x300014
362         return 0;
363 #else
364         unsigned char id1[512], id2[512];
365         size_t id1_size = 512, id2_size = 512;
366         int err;
367
368         err = gnutls_x509_crt_get_authority_key_id(cert, id1, &id1_size, NULL);
369         if (err)
370                 return 0;
371
372         err = gnutls_x509_crt_get_subject_key_id(issuer, id2, &id2_size, NULL);
373         if (err)
374                 return 0;
375         if (id1_size == id2_size && !memcmp(id1, id2, id1_size))
376                 return 0;
377
378         /* EEP! */
379         return -EIO;
380 #endif
381 }
382
383 static int count_x509_certificates(gnutls_datum_t *datum)
384 {
385         int count = 0;
386         char *p = (char *)datum->data;
387
388         while (p) {
389                 p = strstr(p, "-----BEGIN ");
390                 if (!p)
391                         break;
392                 p += 11;
393                 if (!strncmp(p, "CERTIFICATE", 11) ||
394                     !strncmp(p, "X509 CERTIFICATE", 16))
395                     count++;
396         }
397         return count;
398 }
399
400 static int load_certificate(struct openconnect_info *vpninfo)
401 {
402         gnutls_datum_t fdata;
403         gnutls_x509_privkey_t key = NULL;
404         gnutls_x509_crl_t crl = NULL;
405         gnutls_x509_crt_t last_cert, cert = NULL;
406         gnutls_x509_crt_t *extra_certs = NULL, *supporting_certs = NULL;
407         unsigned int nr_supporting_certs, nr_extra_certs = 0;
408         int err; /* GnuTLS error */
409         int ret = 0; /* our error (zero or -errno) */
410         int i;
411         unsigned char key_id[20];
412         size_t key_id_size = sizeof(key_id);
413
414         if (vpninfo->cert_type == CERT_TYPE_TPM) {
415                 vpn_progress(vpninfo, PRG_ERR,
416                              _("TPM support not available with GnuTLS\n"));
417                 return -EINVAL;
418         }
419
420         if (!strncmp(vpninfo->cert, "pkcs11:", 7)) {
421                 vpn_progress(vpninfo, PRG_TRACE,
422                              _("Using PKCS#11 certificate %s\n"), vpninfo->cert);
423
424                 err = gnutls_certificate_set_x509_key_file(vpninfo->https_cred,
425                                                            vpninfo->cert,
426                                                            vpninfo->sslkey,
427                                                            GNUTLS_X509_FMT_PEM);
428                 if (err) {
429                         vpn_progress(vpninfo, PRG_ERR,
430                                      _("Error loading PKCS#11 certificate: %s\n"),
431                                      gnutls_strerror(err));
432                         return -EIO;
433                 }
434                 return 0;
435         }
436
437         vpn_progress(vpninfo, PRG_TRACE,
438                      _("Using certificate file %s\n"), vpninfo->cert);
439
440         ret = load_datum(vpninfo, &fdata, vpninfo->cert);
441         if (ret)
442                 return ret;
443
444         if (vpninfo->cert_type == CERT_TYPE_PKCS12 ||
445             vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
446                 ret = load_pkcs12_certificate(vpninfo, &fdata, &key, &cert,
447                                               &extra_certs, &nr_extra_certs, &crl);
448                 if (ret < 0)
449                         goto out;
450                 else if (!ret)
451                         goto got_cert;
452
453                 /* It returned NOT_PKCS12.
454                    Fall through to try PEM formats. */
455         }
456
457         /* We need to know how many there are in *advance*; it won't just allocate
458            the array for us :( */
459         nr_extra_certs = count_x509_certificates(&fdata);
460         if (!nr_extra_certs)
461                 nr_extra_certs = 1; /* wtf? Oh well, we'll fail later... */
462
463         extra_certs = calloc(nr_extra_certs, sizeof(cert));
464         if (!extra_certs) {
465                 nr_extra_certs = 0;
466                 ret = -ENOMEM;
467                 goto out;
468         }
469         err = gnutls_x509_crt_list_import(extra_certs, &nr_extra_certs, &fdata,
470                                           GNUTLS_X509_FMT_PEM, 0);
471         if (err <= 0) {
472                 const char *reason;
473                 if (!err || err == GNUTLS_E_NO_CERTIFICATE_FOUND)
474                         reason = _("No certificate found in file");
475                 else
476                         reason = gnutls_strerror(err);
477
478                 vpn_progress(vpninfo, PRG_ERR,
479                              _("Loading certificate failed: %s\n"),
480                              reason);
481                 ret = -EINVAL;
482                 goto out;
483         }
484         nr_extra_certs = err;
485         err = 0;
486
487         if (vpninfo->sslkey != vpninfo->cert) {
488                 gnutls_free(fdata.data);
489
490                 vpn_progress(vpninfo, PRG_TRACE,
491                              _("Using private key file %s\n"), vpninfo->cert);
492
493                 ret = load_datum(vpninfo, &fdata, vpninfo->sslkey);
494                 if (ret)
495                         goto out;
496         }
497
498         gnutls_x509_privkey_init(&key);
499         /* Try PKCS#1 (and PKCS#8 without password) first. GnuTLS doesn't
500            support OpenSSL's old PKCS#1-based encrypted format. We should
501            probably check for it and give a more coherent failure mode. */
502         err = gnutls_x509_privkey_import(key, &fdata, GNUTLS_X509_FMT_PEM);
503         if (err) {
504                 /* If that fails, try PKCS#8 */
505                 char *pass = vpninfo->cert_password;
506
507                 /* Yay, just for fun this is *different* to PKCS#12. Where we could
508                    try an empty password there, in this case the empty-password case
509                    has already been *tried* by gnutls_x509_privkey_import(). If we
510                    just call gnutls_x509_privkey_import_pkcs8() with a NULL password,
511                    it'll SEGV. You have to set the GNUTLS_PKCS_PLAIN flag if you want
512                    to try without a password. Passing NULL evidently isn't enough of
513                    a hint. And in GnuTLS 3.1 where that crash has been fixed, passing
514                    NULL will cause it to return GNUTLS_E_ENCRYPTED_STRUCTURE (a new
515                    error code) rather than GNUTLS_E_DECRYPTION_FAILED. So just pass ""
516                    instead of NULL, and don't worry about either case. */
517                 while ((err = gnutls_x509_privkey_import_pkcs8(key, &fdata,
518                                                                GNUTLS_X509_FMT_PEM,
519                                                                pass?pass:"", 0))) {
520                         if (err != GNUTLS_E_DECRYPTION_FAILED) {
521                                 vpn_progress(vpninfo, PRG_ERR,
522                                              _("Failed to load private key as PKCS#8: %s\n"),
523                                              gnutls_strerror(err));
524                                 ret = -EINVAL;
525                                 goto out;
526                         }
527                         if (pass) {
528                                 vpn_progress(vpninfo, PRG_ERR,
529                                              _("Failed to decrypt PKCS#8 certificate file\n"));
530                                 free (pass);
531                         }
532                         err = request_passphrase(vpninfo, &pass,
533                                                  _("Enter PEM pass phrase:"));
534                         if (err) {
535                                 ret = -EINVAL;
536                                 goto out;
537                         }
538                 }
539         }
540         err = gnutls_x509_privkey_get_key_id(key, 0, key_id, &key_id_size);
541         if (err) {
542                 vpn_progress(vpninfo, PRG_ERR,
543                              _("Failed to get key ID: %s\n"),
544                              gnutls_strerror(err));
545                 goto out;
546         }
547         for (i = 0; i < nr_extra_certs; i++) {
548                 unsigned char cert_id[20];
549                 size_t cert_id_size = sizeof(cert_id);
550
551                 err = gnutls_x509_crt_get_key_id(extra_certs[i], 0, cert_id, &cert_id_size);
552                 if (err)
553                         continue;
554
555                 if (cert_id_size == key_id_size && !memcmp(cert_id, key_id, key_id_size)) {
556                         cert = extra_certs[i];
557
558                         /* Move the rest of the array down */
559                         for (; i < nr_extra_certs - 1; i++)
560                                 extra_certs[i] = extra_certs[i+1];
561
562                         nr_extra_certs--;
563                         goto got_cert;
564                 }
565         }
566         /* We shouldn't reach this. It means that we didn't find *any* matching cert */
567         vpn_progress(vpninfo, PRG_ERR,
568                      _("No SSL certificate found to match private key\n"));
569         ret = -EINVAL;
570         goto out;
571
572  got_cert:
573         check_certificate_expiry(vpninfo, cert);
574
575         if (crl) {
576                 err = gnutls_certificate_set_x509_crl(vpninfo->https_cred, &crl, 1);
577                 if (err) {
578                         vpn_progress(vpninfo, PRG_ERR,
579                                      _("Setting certificate recovation list failed: %s\n"),
580                                      gnutls_strerror(err));
581                         goto out;
582                 }
583         }
584
585         /* OpenSSL has problems with certificate chains — if there are
586            multiple certs with the same name, it doesn't necessarily
587            choose the _right_ one. (RT#1942)
588            Pick the right ones for ourselves and add them manually. */
589         last_cert = cert;
590         nr_supporting_certs = 1; /* Our starting cert */
591         while (1) {
592                 gnutls_x509_crt_t issuer;
593
594                 for (i = 0; i < nr_extra_certs; i++) {
595                         if (gnutls_x509_crt_check_issuer(last_cert, extra_certs[i]) &&
596                             !check_issuer_sanity(last_cert, extra_certs[i]))
597                                 break;
598                 }
599
600                 if (i < nr_extra_certs) {
601                         issuer = extra_certs[i];
602                 } else {
603                         err = gnutls_certificate_get_issuer(vpninfo->https_cred,
604                                                             last_cert, &issuer, 0);
605                         if (err)
606                                 break;
607                 }
608
609                 /* The check_issuer_sanity() function works fine as a workaround where
610                    it was used above, but when gnutls_certificate_get_issuer() returns
611                    a bogus cert, there's nothing we can do to fix it up. We don't get
612                    to iterate over all the available certs like we can over our own
613                    list. */
614                 if (check_issuer_sanity(last_cert, issuer)) {
615                         /* Hm, is there a bug reference for this? Or just the git commit
616                            reference (c1ef7efb in master, 5196786c in gnutls_3_0_x-2)? */
617                         vpn_progress(vpninfo, PRG_ERR,
618                                      _("WARNING: GnuTLS returned incorrect issuer certs; authentication may fail!\n"));
619                         break;
620                 }
621
622                 if (issuer == last_cert) {
623                         /* Don't actually include the root CA. If they don't already trust it,
624                            then handing it to them isn't going to help. But don't omit the
625                            original certificate if it's self-signed. */
626                         if (nr_supporting_certs > 1)
627                                 nr_supporting_certs--;
628                         break;
629                 }
630
631                 /* OK, we found a new cert to add to our chain. */
632                 supporting_certs = realloc(supporting_certs,
633                                            sizeof(cert) * ++nr_supporting_certs);
634                 if (!supporting_certs) {
635                         vpn_progress(vpninfo, PRG_ERR,
636                                      _("Failed to allocate memory for supporting certificates\n"));
637                         /* The world is probably about to end, but try without them anyway */
638                         break;
639                 }
640
641                 /* First time we actually allocated an array? Copy the first cert into it */
642                 if (nr_supporting_certs == 2)
643                         supporting_certs[0] = cert;
644
645                 /* Append the new one */
646                 supporting_certs[nr_supporting_certs-1] = issuer;
647                 last_cert = issuer;
648
649         }
650         for (i = 1; i < nr_supporting_certs; i++) {
651                 char name[80];
652                 size_t namelen;
653
654                 sprintf(name, "<unknown>");
655                 namelen = sizeof(name);
656                 if (gnutls_x509_crt_get_dn_by_oid(supporting_certs[i],
657                                                   GNUTLS_OID_X520_COMMON_NAME,
658                                                   0, 0, name, &namelen) &&
659                     gnutls_x509_crt_get_dn(supporting_certs[i], name, &namelen))
660                         sprintf(name, "<unknown>");
661
662                 vpn_progress(vpninfo, PRG_DEBUG,
663                              _("Adding supporting CA '%s'\n"), name);
664         }
665
666         err = gnutls_certificate_set_x509_key(vpninfo->https_cred,
667                                               supporting_certs ? supporting_certs : &cert,
668                                               supporting_certs ? 1 : nr_supporting_certs,
669                                               key);
670         if (err) {
671                 vpn_progress(vpninfo, PRG_ERR,
672                              _("Setting certificate failed: %s\n"),
673                              gnutls_strerror(err));
674                 ret = -EIO;
675         }
676  out:
677         if (crl)
678                 gnutls_x509_crl_deinit(crl);
679         if (key)
680                 gnutls_x509_privkey_deinit(key);
681         if (cert)
682                 gnutls_x509_crt_deinit(cert);
683         for (i = 0; i < nr_extra_certs; i++) {
684                 if (extra_certs[i])
685                         gnutls_x509_crt_deinit(extra_certs[i]);
686         }
687         free(extra_certs);
688         free(supporting_certs);
689         gnutls_free(fdata.data);
690         return ret;
691 }
692
693 static int get_cert_fingerprint(struct openconnect_info *vpninfo,
694                                 gnutls_x509_crt_t cert,
695                                 gnutls_digest_algorithm_t algo,
696                                 char *buf)
697 {
698         unsigned char md[256];
699         size_t md_size = sizeof(md);
700         unsigned int i;
701
702         if (gnutls_x509_crt_get_fingerprint(cert, algo, md, &md_size))
703                 return -EIO;
704
705         for (i=0; i < md_size; i++)
706                 sprintf(&buf[i*2], "%02X", md[i]);
707
708         return 0;
709 }
710
711 int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
712                              OPENCONNECT_X509 *cert, char *buf)
713 {
714         return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_MD5, buf);
715 }
716
717 int openconnect_get_cert_sha1(struct openconnect_info *vpninfo,
718                               OPENCONNECT_X509 *cert, char *buf)
719 {
720         return get_cert_fingerprint(vpninfo, cert, GNUTLS_DIG_SHA1, buf);
721 }
722
723 char *openconnect_get_cert_details(struct openconnect_info *vpninfo,
724                                    OPENCONNECT_X509 *cert)
725 {
726         gnutls_datum_t buf;
727         char *ret;
728
729         if (gnutls_x509_crt_print(cert, GNUTLS_CRT_PRINT_FULL, &buf))
730                 return NULL;
731         
732         /* Just in case gnutls_free() isn't free(), we can't steal it. */
733         ret = strdup((char *)buf.data);
734         gnutls_free(buf.data);
735         
736         return ret;
737 }
738
739 int openconnect_get_cert_DER(struct openconnect_info *vpninfo,
740                              OPENCONNECT_X509 *cert, unsigned char **buf)
741 {
742         size_t l = 0;
743         unsigned char *ret = NULL;
744
745         if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l) != 
746             GNUTLS_E_SHORT_MEMORY_BUFFER)
747                 return -EIO;
748
749         ret = malloc(l);
750         if (!ret)
751                 return -ENOMEM;
752
753         if (gnutls_x509_crt_export(cert, GNUTLS_X509_FMT_DER, ret, &l)) {
754                 free(ret);
755                 return -EIO;
756         }
757         *buf = ret;
758         return l;
759 }
760
761 static int verify_peer(gnutls_session_t session)
762 {
763         struct openconnect_info *vpninfo = gnutls_session_get_ptr(session);
764         const gnutls_datum_t *cert_list;
765         gnutls_x509_crt_t cert;
766         unsigned int status, cert_list_size;
767         const char *reason = NULL;
768         int err;
769
770         if (vpninfo->peer_cert) {
771                 gnutls_x509_crt_deinit(vpninfo->peer_cert);
772                 vpninfo->peer_cert = NULL;
773         }
774
775         cert_list = gnutls_certificate_get_peers (session, &cert_list_size);
776         if (!cert_list) {
777                 vpn_progress(vpninfo, PRG_ERR, _("Server presented no certificate\n"));
778                 return GNUTLS_E_CERTIFICATE_ERROR;
779         }
780
781         if (vpninfo->servercert) {
782                 unsigned char sha1bin[SHA1_SIZE];
783                 char fingerprint[(SHA1_SIZE * 2) + 1];
784                 int i;
785                 
786                 err = openconnect_sha1(sha1bin, cert_list[0].data, cert_list[0].size);
787                 if (err) {
788                         vpn_progress(vpninfo, PRG_ERR,
789                                      _("Could not calculate SHA1 of server's certificate\n"));
790                         return GNUTLS_E_CERTIFICATE_ERROR;
791                 }
792                 for (i=0; i < SHA1_SIZE; i++)
793                         sprintf(&fingerprint[i*2], "%02X", sha1bin[i]);
794
795                 if (strcasecmp(vpninfo->servercert, fingerprint)) {
796                         vpn_progress(vpninfo, PRG_ERR,
797                                      _("Server SSL certificate didn't match: %s\n"), fingerprint);
798                         return GNUTLS_E_CERTIFICATE_ERROR;
799                 }
800                 return 0;
801         }
802
803         err = gnutls_certificate_verify_peers2 (session, &status);
804         if (err) {
805                 vpn_progress(vpninfo, PRG_ERR, _("Error checking server cert status\n"));
806                 return GNUTLS_E_CERTIFICATE_ERROR;
807         }
808
809         if (status & GNUTLS_CERT_REVOKED)
810                 reason = _("certificate revoked");
811         else if (status & GNUTLS_CERT_SIGNER_NOT_FOUND)
812                 reason = _("signer not found");
813         else if (status & GNUTLS_CERT_SIGNER_NOT_CA)
814                 reason = _("signer not a CA certificate");
815         else if (status & GNUTLS_CERT_INSECURE_ALGORITHM)
816                 reason = _("insecure algorithm");
817         else if (status & GNUTLS_CERT_NOT_ACTIVATED)
818                 reason = _("certificate not yet activated");
819         else if (status & GNUTLS_CERT_EXPIRED)
820                 reason = _("certificate expired");
821         else if (status & GNUTLS_CERT_INVALID)
822                 /* If this is set and no other reason, it apparently means
823                    that signature verification failed. Not entirely sure
824                    why we don't just set a bit for that too. */
825                 reason = _("signature verification failed");
826
827         err = gnutls_x509_crt_init(&cert);
828         if (err) {
829                 vpn_progress(vpninfo, PRG_ERR, _("Error initialising X509 cert structure\n"));
830                 return GNUTLS_E_CERTIFICATE_ERROR;
831         }
832
833         err = gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER);
834         if (err) {
835                 vpn_progress(vpninfo, PRG_ERR, _("Error importing server's cert\n"));
836                 gnutls_x509_crt_deinit(cert);
837                 return GNUTLS_E_CERTIFICATE_ERROR;
838         }
839
840         if (!reason && !gnutls_x509_crt_check_hostname(cert, vpninfo->hostname))
841                 reason = _("certificate does not match hostname");
842
843         if (reason) {
844                 vpn_progress(vpninfo, PRG_INFO, "Server certificate verify failed: %s\n",
845                              reason);
846                 if (vpninfo->validate_peer_cert)
847                         err = vpninfo->validate_peer_cert(vpninfo->cbdata,
848                                                           cert,
849                                                           reason) ? GNUTLS_E_CERTIFICATE_ERROR : 0;
850                 else
851                         err = GNUTLS_E_CERTIFICATE_ERROR;
852         }
853
854         vpninfo->peer_cert = cert;
855
856         return err;
857 }
858
859
860 int openconnect_open_https(struct openconnect_info *vpninfo)
861 {
862         int ssl_sock = -1;
863         int err;
864
865         if (vpninfo->https_sess)
866                 return 0;
867
868         ssl_sock = connect_https_socket(vpninfo);
869         if (ssl_sock < 0)
870                 return ssl_sock;
871
872         if (!vpninfo->https_cred) {
873                 gnutls_certificate_allocate_credentials(&vpninfo->https_cred);
874                 gnutls_certificate_set_x509_trust_file(vpninfo->https_cred,
875                                                        "/etc/pki/tls/certs/ca-bundle.crt",
876                                                        GNUTLS_X509_FMT_PEM);
877                 gnutls_certificate_set_verify_function (vpninfo->https_cred,
878                                                         verify_peer);
879                 /* FIXME: Ensure TLSv1.0, no options */
880
881                 if (vpninfo->cafile) {
882                         err = gnutls_certificate_set_x509_trust_file(vpninfo->https_cred,
883                                                                      vpninfo->cafile,
884                                                                      GNUTLS_X509_FMT_PEM);
885                         if (err < 0) {
886                                 vpn_progress(vpninfo, PRG_ERR,
887                                              _("Failed to open CA file '%s': %s\n"),
888                                              vpninfo->cafile, gnutls_strerror(err));
889                                 close(ssl_sock);
890                                 return -EINVAL;
891                         }
892                 }
893
894                 if (vpninfo->cert) {
895                         err = load_certificate(vpninfo);
896                         if (err) {
897                                 vpn_progress(vpninfo, PRG_ERR,
898                                              _("Loading certificate failed. Aborting.\n"));
899                                 return err;
900                         }
901                 }
902
903                 /* We just want to do:
904                    SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY); 
905                    ... but it doesn't work with OpenSSL < 0.9.8k because of 
906                    problems with inheritance (fixed in v1.1.4.6 of
907                    crypto/x509/x509_vpm.c) so we have to play silly buggers
908                    instead. This trick doesn't work _either_ in < 0.9.7 but
909                    I don't know of _any_ workaround which will, and can't
910                    be bothered to find out either. */
911
912
913         }
914         gnutls_init (&vpninfo->https_sess, GNUTLS_CLIENT);
915         gnutls_session_set_ptr (vpninfo->https_sess, (void *) vpninfo);
916         err = gnutls_priority_set_direct (vpninfo->https_sess, "NONE:+VERS-TLS1.0:+SHA1:+AES-128-CBC:+RSA:+COMP-NULL:%COMPAT:%DISABLE_SAFE_RENEGOTIATION", NULL);
917         if (err) {
918                 vpn_progress(vpninfo, PRG_ERR,
919                              _("Failed to set TLS priority string: %s\n"),
920                              gnutls_strerror(err));
921                 return -EIO;
922         }
923
924         gnutls_record_disable_padding (vpninfo->https_sess);
925         gnutls_credentials_set (vpninfo->https_sess, GNUTLS_CRD_CERTIFICATE, vpninfo->https_cred);
926         gnutls_transport_set_ptr(vpninfo->https_sess, /* really? */(gnutls_transport_ptr_t)(long) ssl_sock);
927
928         vpn_progress(vpninfo, PRG_INFO, _("SSL negotiation with %s\n"),
929                      vpninfo->hostname);
930
931         while ((err = gnutls_handshake (vpninfo->https_sess))) {
932                 if (err == GNUTLS_E_AGAIN) {
933                         fd_set rd_set, wr_set;
934                         int maxfd = ssl_sock;
935                         
936                         FD_ZERO(&rd_set);
937                         FD_ZERO(&wr_set);
938                         
939                         if (gnutls_record_get_direction(vpninfo->https_sess))
940                                 FD_SET(ssl_sock, &wr_set);
941                         else
942                                 FD_SET(ssl_sock, &rd_set);
943
944                         if (vpninfo->cancel_fd != -1) {
945                                 FD_SET(vpninfo->cancel_fd, &rd_set);
946                                 if (vpninfo->cancel_fd > ssl_sock)
947                                         maxfd = vpninfo->cancel_fd;
948                         }
949                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
950                         if (vpninfo->cancel_fd != -1 &&
951                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
952                                 vpn_progress(vpninfo, PRG_ERR, _("SSL connection cancelled\n"));
953                                 gnutls_deinit(vpninfo->https_sess);
954                                 vpninfo->https_sess = NULL;
955                                 close(ssl_sock);
956                                 return -EINTR;
957                         }
958                 } else if (err == GNUTLS_E_INTERRUPTED || gnutls_error_is_fatal(err)) {
959                         vpn_progress(vpninfo, PRG_ERR, _("SSL connection failure: %s\n"),
960                                                          gnutls_strerror(err));
961                         gnutls_deinit(vpninfo->https_sess);
962                         vpninfo->https_sess = NULL;
963                         close(ssl_sock);
964                         return -EIO;
965                 } else {
966                         /* non-fatal error or warning. Ignore it and continue */
967                         vpn_progress(vpninfo, PRG_TRACE,
968                                      _("GnuTLS non-fatal return during handshake: %s\n"),
969                                      gnutls_strerror(err));
970                 }
971         }
972
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 (vpninfo->peer_cert) {
984                 gnutls_x509_crt_deinit(vpninfo->peer_cert);
985                 vpninfo->peer_cert = NULL;
986         }
987         if (vpninfo->https_sess) {
988                 gnutls_deinit(vpninfo->https_sess);
989                 vpninfo->https_sess = NULL;
990         }
991         if (vpninfo->ssl_fd != -1) {
992                 close(vpninfo->ssl_fd);
993                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_rfds);
994                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
995                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_efds);
996                 vpninfo->ssl_fd = -1;
997         }
998 }
999
1000 void openconnect_init_openssl(void)
1001 {
1002         gnutls_global_init();
1003 }
1004
1005 int openconnect_sha1(unsigned char *result, void *data, int datalen)
1006 {
1007         gnutls_datum_t d;
1008         size_t shalen = SHA1_SIZE;
1009
1010         d.data = data;
1011         d.size = datalen;
1012         if (gnutls_fingerprint(GNUTLS_DIG_SHA1, &d, result, &shalen))
1013                 return -1;
1014
1015         return 0;
1016 }
1017
1018 int openconnect_random(void *bytes, int len)
1019 {
1020         if (gnutls_rnd(GNUTLS_RND_RANDOM, bytes, len))
1021                 return -EIO;
1022         return 0;
1023 }
1024
1025 int openconnect_local_cert_md5(struct openconnect_info *vpninfo,
1026                                char *buf)
1027 {
1028         const gnutls_datum_t *d;
1029         size_t md5len = 16;
1030
1031         buf[0] = 0;
1032
1033         d = gnutls_certificate_get_ours(vpninfo->https_sess);
1034         if (!d)
1035                 return -EIO;
1036
1037         if (gnutls_fingerprint(GNUTLS_DIG_MD5, d, buf, &md5len))
1038                 return -EIO;
1039
1040         return 0;
1041 }
1042