Return error from OpenSSL load_certificate() for PKCS#11 URLs
[platform/upstream/openconnect.git] / openssl.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 <errno.h>
26 #include <sys/types.h>
27 #include <netinet/in.h>
28 #include <arpa/inet.h>
29 #include <ctype.h>
30
31 #include <openssl/ssl.h>
32 #include <openssl/err.h>
33 #include <openssl/engine.h>
34 #include <openssl/evp.h>
35 #include <openssl/rand.h>
36 #include <openssl/pkcs12.h>
37 #include <openssl/x509v3.h>
38 #include <openssl/x509.h>
39 #include <openssl/bio.h>
40
41 #include "openconnect-internal.h"
42
43 int openconnect_sha1(unsigned char *result, void *data, int len)
44 {
45         EVP_MD_CTX c;
46
47         EVP_MD_CTX_init(&c);
48         EVP_Digest(data, len, result, NULL, EVP_sha1(), NULL);
49         EVP_MD_CTX_cleanup(&c);
50
51         return 0;
52 }
53
54 int openconnect_get_cert_DER(struct openconnect_info *vpninfo,
55                              OPENCONNECT_X509 *cert, unsigned char **buf)
56 {
57         BIO *bp = BIO_new(BIO_s_mem());
58         BUF_MEM *certinfo;
59         size_t l;
60
61         if (!i2d_X509_bio(bp, cert)) {
62                 BIO_free(bp);
63                 return -EIO;
64         }
65
66         BIO_get_mem_ptr(bp, &certinfo);
67         l = certinfo->length;
68         *buf = malloc(l);
69         if (!*buf) {
70                 BIO_free(bp);
71                 return -ENOMEM;
72         }
73         memcpy(*buf, certinfo->data, l);
74         BIO_free(bp);
75         return l;
76 }
77
78 int openconnect_random(void *bytes, int len)
79 {
80         if (RAND_bytes(bytes, len) != 1)
81                 return -EIO;
82         return 0;
83 }
84
85 /* Helper functions for reading/writing lines over SSL.
86    We could use cURL for the HTTP stuff, but it's overkill */
87
88 int openconnect_SSL_write(struct openconnect_info *vpninfo, char *buf, size_t len)
89 {
90         size_t orig_len = len;
91
92         while (len) {
93                 int done = SSL_write(vpninfo->https_ssl, buf, len);
94
95                 if (done > 0)
96                         len -= done;
97                 else {
98                         int err = SSL_get_error(vpninfo->https_ssl, done);
99                         fd_set wr_set, rd_set;
100                         int maxfd = vpninfo->ssl_fd;
101
102                         FD_ZERO(&wr_set);
103                         FD_ZERO(&rd_set);
104                         
105                         if (err == SSL_ERROR_WANT_READ)
106                                 FD_SET(vpninfo->ssl_fd, &rd_set);
107                         else if (err == SSL_ERROR_WANT_WRITE)
108                                 FD_SET(vpninfo->ssl_fd, &wr_set);
109                         else {
110                                 vpn_progress(vpninfo, PRG_ERR, _("Failed to write to SSL socket"));
111                                 openconnect_report_ssl_errors(vpninfo);
112                                 return -EIO;
113                         }
114                         if (vpninfo->cancel_fd != -1) {
115                                 FD_SET(vpninfo->cancel_fd, &rd_set);
116                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
117                                         maxfd = vpninfo->cancel_fd;
118                         }
119                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
120                         if (vpninfo->cancel_fd != -1 &&
121                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
122                                 vpn_progress(vpninfo, PRG_ERR, _("SSL write cancelled\n"));
123                                 return -EINTR;
124                         }
125                 }
126         }
127         return orig_len;
128 }
129
130 int openconnect_SSL_read(struct openconnect_info *vpninfo, char *buf, size_t len)
131 {
132         int done;
133
134         while ((done = SSL_read(vpninfo->https_ssl, buf, len)) == -1) {
135                 int err = SSL_get_error(vpninfo->https_ssl, done);
136                 fd_set wr_set, rd_set;
137                 int maxfd = vpninfo->ssl_fd;
138
139                 FD_ZERO(&wr_set);
140                 FD_ZERO(&rd_set);
141                         
142                 if (err == SSL_ERROR_WANT_READ)
143                         FD_SET(vpninfo->ssl_fd, &rd_set);
144                 else if (err == SSL_ERROR_WANT_WRITE)
145                         FD_SET(vpninfo->ssl_fd, &wr_set);
146                 else {
147                         vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket"));
148                         openconnect_report_ssl_errors(vpninfo);
149                         return -EIO;
150                 }
151                 if (vpninfo->cancel_fd != -1) {
152                         FD_SET(vpninfo->cancel_fd, &rd_set);
153                         if (vpninfo->cancel_fd > vpninfo->ssl_fd)
154                                 maxfd = vpninfo->cancel_fd;
155                 }
156                 select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
157                 if (vpninfo->cancel_fd != -1 &&
158                     FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
159                         vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
160                         return -EINTR;
161                 }
162         }
163         return done;
164 }
165
166 int openconnect_SSL_gets(struct openconnect_info *vpninfo, char *buf, size_t len)
167 {
168         int i = 0;
169         int ret;
170
171         if (len < 2)
172                 return -EINVAL;
173
174         while (1) {
175                 ret = SSL_read(vpninfo->https_ssl, buf + i, 1);
176                 if (ret == 1) {
177                         if (buf[i] == '\n') {
178                                 buf[i] = 0;
179                                 if (i && buf[i-1] == '\r') {
180                                         buf[i-1] = 0;
181                                         i--;
182                                 }
183                                 return i;
184                         }
185                         i++;
186
187                         if (i >= len - 1) {
188                                 buf[i] = 0;
189                                 return i;
190                         }
191                 } else {
192                         fd_set rd_set, wr_set;
193                         int maxfd = vpninfo->ssl_fd;
194                         
195                         FD_ZERO(&rd_set);
196                         FD_ZERO(&wr_set);
197                         
198                         ret = SSL_get_error(vpninfo->https_ssl, ret);
199                         if (ret == SSL_ERROR_WANT_READ)
200                                 FD_SET(vpninfo->ssl_fd, &rd_set);
201                         else if (ret == SSL_ERROR_WANT_WRITE)
202                                 FD_SET(vpninfo->ssl_fd, &wr_set);
203                         else {
204                                 vpn_progress(vpninfo, PRG_ERR, _("Failed to read from SSL socket\n"));
205                                 openconnect_report_ssl_errors(vpninfo);
206                                 ret = -EIO;
207                                 break;
208                         }
209                         if (vpninfo->cancel_fd != -1) {
210                                 FD_SET(vpninfo->cancel_fd, &rd_set);
211                                 if (vpninfo->cancel_fd > vpninfo->ssl_fd)
212                                         maxfd = vpninfo->cancel_fd;
213                         }
214                         select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
215                         if (vpninfo->cancel_fd != -1 &&
216                             FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
217                                 vpn_progress(vpninfo, PRG_ERR, _("SSL read cancelled\n"));
218                                 ret = -EINTR;
219                                 break;
220                         }
221                 }
222         }
223         buf[i] = 0;
224         return i ?: ret;
225 }
226
227
228 /* UI handling. All this just to handle the PIN callback from the TPM ENGINE,
229    and turn it into a call to our ->process_auth_form function */
230
231 struct ui_data {
232         struct openconnect_info *vpninfo;
233         struct oc_form_opt **last_opt;
234         struct oc_auth_form form;
235 };
236
237 struct ui_form_opt {
238         struct oc_form_opt opt;
239         UI_STRING *uis;
240 };
241
242  /* Ick. But there is no way to pass this sanely through OpenSSL */
243 static struct openconnect_info *ui_vpninfo;
244
245 static int ui_open(UI *ui)
246 {
247         struct openconnect_info *vpninfo = ui_vpninfo; /* Ick */
248         struct ui_data *ui_data;
249
250         if (!vpninfo || !vpninfo->process_auth_form)
251                 return 0;
252         
253         ui_data = malloc(sizeof(*ui_data));
254         if (!ui_data)
255                 return 0;
256
257         memset(ui_data, 0, sizeof(*ui_data));
258         ui_data->last_opt = &ui_data->form.opts;
259         ui_data->vpninfo = vpninfo;
260         UI_add_user_data(ui, ui_data);
261
262         return 1;
263 }
264
265 static int ui_write(UI *ui, UI_STRING *uis)
266 {
267         struct ui_data *ui_data = UI_get0_user_data(ui);
268         struct ui_form_opt *opt;
269
270         switch(UI_get_string_type(uis)) {
271         case UIT_ERROR:
272                 ui_data->form.error = (char *)UI_get0_output_string(uis);
273                 break;
274         case UIT_INFO:
275                 ui_data->form.message = (char *)UI_get0_output_string(uis);
276                 break;
277         case UIT_PROMPT:
278                 opt = malloc(sizeof(*opt));
279                 if (!opt)
280                         return 1;
281                 memset(opt, 0, sizeof(*opt));
282                 opt->uis = uis;
283                 opt->opt.label = opt->opt.name = (char *)UI_get0_output_string(uis);
284                 if (UI_get_input_flags(uis) & UI_INPUT_FLAG_ECHO)
285                         opt->opt.type = OC_FORM_OPT_TEXT;
286                 else
287                         opt->opt.type = OC_FORM_OPT_PASSWORD;
288                 *(ui_data->last_opt) = &opt->opt;
289                 ui_data->last_opt = &opt->opt.next;
290                 break;
291
292         default:
293                 fprintf(stderr, "Unhandled SSL UI request type %d\n",
294                         UI_get_string_type(uis));
295                 return 0;
296         }
297         return 1;
298 }
299
300 static int ui_flush(UI *ui)
301 {
302         struct ui_data *ui_data = UI_get0_user_data(ui);
303         struct openconnect_info *vpninfo = ui_data->vpninfo;
304         struct ui_form_opt *opt;
305         int ret;
306
307         ret = vpninfo->process_auth_form(vpninfo->cbdata, &ui_data->form);
308         if (ret)
309                 return 0;
310
311         for (opt = (struct ui_form_opt *)ui_data->form.opts; opt;
312              opt = (struct ui_form_opt *)opt->opt.next) {
313                 if (opt->opt.value && opt->uis)
314                         UI_set_result(ui, opt->uis, opt->opt.value);
315         }
316         return 1;
317 }
318
319 static int ui_close(UI *ui)
320 {
321         struct ui_data *ui_data = UI_get0_user_data(ui);
322         struct ui_form_opt *opt, *next_opt;
323
324         opt = (struct ui_form_opt *)ui_data->form.opts;
325         while (opt) {
326                 next_opt = (struct ui_form_opt *)opt->opt.next;
327                 if (opt->opt.value)
328                         free(opt->opt.value);
329                 free(opt);
330                 opt = next_opt;
331         }
332         free(ui_data);
333         UI_add_user_data(ui, NULL);
334
335         return 1;
336 }
337
338 static UI_METHOD *create_openssl_ui(struct openconnect_info *vpninfo)
339 {
340         UI_METHOD *ui_method = UI_create_method((char *)"AnyConnect VPN UI");
341
342         /* There is a race condition here because of the use of the
343            static ui_vpninfo pointer. This sucks, but it's OpenSSL's
344            fault and in practice it's *never* going to hurt us.
345
346            This UI is only used for loading certificates from a TPM; for
347            PKCS#12 and PEM files we hook the passphrase request differently.
348            The ui_vpninfo variable is set here, and is used from ui_open()
349            when the TPM ENGINE decides it needs to ask the user for a PIN.
350
351            The race condition exists because theoretically, there
352            could be more than one thread using libopenconnect and
353            trying to authenticate to a VPN server, within the *same*
354            process. And if *both* are using certificates from the TPM,
355            and *both* manage to be within that short window of time
356            between setting ui_vpninfo and invoking ui_open() to fetch
357            the PIN, then one connection's ->process_auth_form() could
358            get a PIN request for the *other* connection. 
359
360            However, the only thing that ever does run libopenconnect more
361            than once from the same process is KDE's NetworkManager support,
362            and NetworkManager doesn't *support* having more than one VPN
363            connected anyway, so first that would have to be fixed and then
364            you'd have to connect to two VPNs simultaneously by clicking
365            'connect' on both at *exactly* the same time and then getting
366            *really* unlucky.
367
368            Oh, and the KDE support won't be using OpenSSL anyway because of
369            licensing conflicts... so although this sucks, I'm not going to
370            lose sleep over it.
371         */
372         ui_vpninfo = vpninfo;
373
374         /* Set up a UI method of our own for password/passphrase requests */
375         UI_method_set_opener(ui_method, ui_open);
376         UI_method_set_writer(ui_method, ui_write);
377         UI_method_set_flusher(ui_method, ui_flush);
378         UI_method_set_closer(ui_method, ui_close);
379
380         return ui_method;
381 }
382
383 static int pem_pw_cb(char *buf, int len, int w, void *v)
384 {
385         struct openconnect_info *vpninfo = v;
386         char *pass = NULL;
387         int plen;
388
389         if (vpninfo->cert_password) {
390                 pass = vpninfo->cert_password;
391                 vpninfo->cert_password = NULL;
392         } else if (request_passphrase(vpninfo, &pass,
393                                       _("Enter PEM pass phrase:")))
394                 return -1;
395
396         plen = strlen(pass);
397
398         if (len <= plen) {
399                 vpn_progress(vpninfo, PRG_ERR,
400                              _("PEM password too long (%d >= %d)\n"),
401                              plen, len);
402                 free(pass);
403                 return -1;
404         }
405
406         memcpy(buf, pass, plen+1);
407         free(pass);
408         return plen;
409 }
410
411 static int load_pkcs12_certificate(struct openconnect_info *vpninfo, PKCS12 *p12)
412 {
413         EVP_PKEY *pkey = NULL;
414         X509 *cert = NULL;
415         STACK_OF(X509) *ca;
416         int ret = 0;
417         char *pass;
418
419         pass = vpninfo->cert_password;
420         vpninfo->cert_password = NULL;
421  retrypass:
422         /* We do this every time round the loop, to work around a bug in
423            OpenSSL < 1.0.0-beta2 -- where the stack at *ca will be freed
424            when PKCS12_parse() returns an error, but *ca is left pointing
425            to the freed memory. */
426         ca = NULL;
427         if (!pass && request_passphrase(vpninfo, &pass,
428                                         _("Enter PKCS#12 pass phrase:")) < 0) {
429                 PKCS12_free(p12);
430                 return -EINVAL;
431         }
432         if (!PKCS12_parse(p12, pass, &pkey, &cert, &ca)) {
433                 unsigned long err = ERR_peek_error();
434
435                 openconnect_report_ssl_errors(vpninfo);
436
437                 if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
438                     ERR_GET_FUNC(err) == PKCS12_F_PKCS12_PARSE &&
439                     ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
440                         vpn_progress(vpninfo, PRG_ERR,
441                                      _("Parse PKCS#12 failed (wrong passphrase?)\n"));
442                         free(pass);
443                         pass = NULL;
444                         goto retrypass;
445                 }
446
447                 vpn_progress(vpninfo, PRG_ERR,
448                              _("Parse PKCS#12 failed (see above errors)\n"));
449                 PKCS12_free(p12);
450                 return -EINVAL;
451         }
452         if (cert) {
453                 char buf[200];
454                 vpninfo->cert_x509 = cert;
455                 SSL_CTX_use_certificate(vpninfo->https_ctx, cert);
456                 X509_NAME_oneline(X509_get_subject_name(cert), buf, sizeof(buf));
457                 vpn_progress(vpninfo, PRG_INFO,
458                              _("Using client certificate '%s'\n"), buf);
459         } else {
460                 vpn_progress(vpninfo, PRG_ERR,
461                              _("PKCS#12 contained no certificate!"));
462                 ret = -EINVAL;
463         }
464
465         if (pkey) {
466                 SSL_CTX_use_PrivateKey(vpninfo->https_ctx, pkey);
467                 EVP_PKEY_free(pkey);
468         } else {
469                 vpn_progress(vpninfo, PRG_ERR,
470                              _("PKCS#12 contained no private key!"));
471                 ret = -EINVAL;
472         }
473
474         /* Only include supporting certificates which are actually necessary */
475         if (ca) {
476                 int i;
477         next:
478                 for (i = 0; i < sk_X509_num(ca); i++) {
479                         X509 *cert2 = sk_X509_value(ca, i);
480                         if (X509_check_issued(cert2, cert) == X509_V_OK) {
481                                 char buf[200];
482
483                                 if (cert2 == cert)
484                                         break;
485                                 if (X509_check_issued(cert2, cert2) == X509_V_OK)
486                                         break;
487
488                                 X509_NAME_oneline(X509_get_subject_name(cert2),
489                                                   buf, sizeof(buf));
490                                 vpn_progress(vpninfo, PRG_DEBUG,
491                                              _("Extra cert from PKCS#12: '%s'\n"), buf);
492                                 CRYPTO_add(&cert2->references, 1, CRYPTO_LOCK_X509);
493                                 SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert2);
494                                 cert = cert2;
495                                 goto next;
496                         }
497                 }
498                 sk_X509_pop_free(ca, X509_free);
499         }
500
501         PKCS12_free(p12);
502         return ret;
503 }
504
505 #ifdef HAVE_ENGINE
506 static int load_tpm_certificate(struct openconnect_info *vpninfo)
507 {
508         ENGINE *e;
509         EVP_PKEY *key;
510         UI_METHOD *meth = NULL;
511         ENGINE_load_builtin_engines();
512
513         e = ENGINE_by_id("tpm");
514         if (!e) {
515                 vpn_progress(vpninfo, PRG_ERR, _("Can't load TPM engine.\n"));
516                 openconnect_report_ssl_errors(vpninfo);
517                 return -EINVAL;
518         }
519         if (!ENGINE_init(e) || !ENGINE_set_default_RSA(e) ||
520             !ENGINE_set_default_RAND(e)) {
521                 vpn_progress(vpninfo, PRG_ERR, _("Failed to init TPM engine\n"));
522                 openconnect_report_ssl_errors(vpninfo);
523                 ENGINE_free(e);
524                 return -EINVAL;
525         }
526
527         if (vpninfo->cert_password) {
528                 if (!ENGINE_ctrl_cmd(e, "PIN", strlen(vpninfo->cert_password),
529                                      vpninfo->cert_password, NULL, 0)) {
530                         vpn_progress(vpninfo, PRG_ERR,
531                                      _("Failed to set TPM SRK password\n"));
532                         openconnect_report_ssl_errors(vpninfo);
533                 }
534         } else {
535                 /* Provide our own UI method to handle the PIN callback. */
536                 meth = create_openssl_ui(vpninfo);
537         }
538         key = ENGINE_load_private_key(e, vpninfo->sslkey, meth, NULL);
539         if (meth)
540                 UI_destroy_method(meth);
541         if (!key) {
542                 vpn_progress(vpninfo, PRG_ERR,
543                              _("Failed to load TPM private key\n"));
544                 openconnect_report_ssl_errors(vpninfo);
545                 ENGINE_free(e);
546                 ENGINE_finish(e);
547                 return -EINVAL;
548         }
549         if (!SSL_CTX_use_PrivateKey(vpninfo->https_ctx, key)) {
550                 vpn_progress(vpninfo, PRG_ERR, _("Add key from TPM failed\n"));
551                 openconnect_report_ssl_errors(vpninfo);
552                 ENGINE_free(e);
553                 ENGINE_finish(e);
554                 return -EINVAL;
555         }
556         return 0;
557 }
558 #else
559 static int load_tpm_certificate(struct openconnect_info *vpninfo)
560 {
561         vpn_progress(vpninfo, PRG_ERR,
562                      _("This version of OpenConnect was built without TPM support\n"));
563         return -EINVAL;
564 }
565 #endif
566
567 static int reload_pem_cert(struct openconnect_info *vpninfo)
568 {
569         BIO *b = BIO_new(BIO_s_file_internal());
570         char buf[200];
571
572         if (!b)
573                 return -ENOMEM;
574
575         if (BIO_read_filename(b, vpninfo->cert) <= 0) {
576         err:
577                 BIO_free(b);
578                 vpn_progress(vpninfo, PRG_ERR,
579                              _("Failed to reload X509 cert for expiry check\n"));
580                 openconnect_report_ssl_errors(vpninfo);
581                 return -EIO;
582         }
583         vpninfo->cert_x509 = PEM_read_bio_X509_AUX(b, NULL, NULL, NULL);
584         if (!vpninfo->cert_x509)
585                 goto err;
586
587         X509_NAME_oneline(X509_get_subject_name(vpninfo->cert_x509), buf, sizeof(buf));
588         vpn_progress(vpninfo, PRG_INFO,
589                              _("Using client certificate '%s'\n"), buf);
590
591         return 0;
592 }
593
594 static int load_certificate(struct openconnect_info *vpninfo)
595 {
596         if (!strncmp(vpninfo->sslkey, "pkcs11:", 7) ||
597             !strncmp(vpninfo->cert, "pkcs11:", 7)) {
598                 vpn_progress(vpninfo, PRG_ERR,
599                              _("This binary built without PKCS#11 support\n"));
600                 return -EINVAL;
601         }
602                      
603         vpn_progress(vpninfo, PRG_TRACE,
604                      _("Using certificate file %s\n"), vpninfo->cert);
605
606         if (vpninfo->cert_type == CERT_TYPE_PKCS12 ||
607             vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
608                 FILE *f;
609                 PKCS12 *p12;
610
611                 f = fopen(vpninfo->cert, "r");
612                 if (!f) {
613                         vpn_progress(vpninfo, PRG_ERR,
614                                      _("Failed to open certificate file %s: %s\n"),
615                                      vpninfo->cert, strerror(errno));
616                         return -ENOENT;
617                 }
618                 p12 = d2i_PKCS12_fp(f, NULL);
619                 fclose(f);
620                 if (p12)
621                         return load_pkcs12_certificate(vpninfo, p12);
622
623                 /* Not PKCS#12 */
624                 if (vpninfo->cert_type == CERT_TYPE_PKCS12) {
625                         vpn_progress(vpninfo, PRG_ERR, _("Read PKCS#12 failed\n"));
626                         openconnect_report_ssl_errors(vpninfo);
627                         return -EINVAL;
628                 }
629                 /* Clear error and fall through to see if it's a PEM file... */
630                 ERR_clear_error();
631         }
632
633         /* It's PEM or TPM now, and either way we need to load the plain cert: */
634         if (!SSL_CTX_use_certificate_chain_file(vpninfo->https_ctx,
635                                                 vpninfo->cert)) {
636                 vpn_progress(vpninfo, PRG_ERR,
637                              _("Loading certificate failed\n"));
638                 openconnect_report_ssl_errors(vpninfo);
639                 return -EINVAL;
640         }
641
642         /* Ew, we can't get it back from the OpenSSL CTX in any sane fashion */
643         reload_pem_cert(vpninfo);
644
645         if (vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
646                 FILE *f = fopen(vpninfo->sslkey, "r");
647                 char buf[256];
648
649                 if (!f) {
650                         vpn_progress(vpninfo, PRG_ERR,
651                                      _("Failed to open private key file %s: %s\n"),
652                                      vpninfo->cert, strerror(errno));
653                         return -ENOENT;
654                 }
655
656                 buf[255] = 0;
657                 while (fgets(buf, 255, f)) {
658                         if (!strcmp(buf, "-----BEGIN TSS KEY BLOB-----\n")) {
659                                 vpninfo->cert_type = CERT_TYPE_TPM;
660                                 break;
661                         } else if (!strcmp(buf, "-----BEGIN RSA PRIVATE KEY-----\n") ||
662                                    !strcmp(buf, "-----BEGIN DSA PRIVATE KEY-----\n") ||
663                                    !strcmp(buf, "-----BEGIN ENCRYPTED PRIVATE KEY-----\n")) {
664                                 vpninfo->cert_type = CERT_TYPE_PEM;
665                                 break;
666                         }
667                 }
668                 fclose(f);
669                 if (vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
670                         vpn_progress(vpninfo, PRG_ERR,
671                                      _("Failed to identify private key type in '%s'\n"),
672                                      vpninfo->sslkey);
673                         return -EINVAL;
674                 }
675         }
676
677         if (vpninfo->cert_type == CERT_TYPE_TPM)
678                 return load_tpm_certificate(vpninfo);
679
680         /* Standard PEM certificate */
681         SSL_CTX_set_default_passwd_cb(vpninfo->https_ctx, pem_pw_cb);
682         SSL_CTX_set_default_passwd_cb_userdata(vpninfo->https_ctx, vpninfo);
683  again:
684         if (!SSL_CTX_use_RSAPrivateKey_file(vpninfo->https_ctx, vpninfo->sslkey,
685                                             SSL_FILETYPE_PEM)) {
686                 unsigned long err = ERR_peek_error();
687                 
688                 openconnect_report_ssl_errors(vpninfo);
689
690 #ifndef EVP_F_EVP_DECRYPTFINAL_EX
691 #define EVP_F_EVP_DECRYPTFINAL_EX EVP_F_EVP_DECRYPTFINAL
692 #endif
693                 /* If the user fat-fingered the passphrase, try again */
694                 if (ERR_GET_LIB(err) == ERR_LIB_EVP &&
695                     ERR_GET_FUNC(err) == EVP_F_EVP_DECRYPTFINAL_EX &&
696                     ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT) {
697                         vpn_progress(vpninfo, PRG_ERR,
698                                      _("Loading private key failed (wrong passphrase?)\n"));
699                         goto again;
700                 }
701                 
702                 vpn_progress(vpninfo, PRG_ERR,
703                              _("Loading private key failed (see above errors)\n"));
704                 return -EINVAL;
705         }
706         return 0;
707 }
708
709 static int get_cert_fingerprint(struct openconnect_info *vpninfo,
710                                 OPENCONNECT_X509 *cert, const EVP_MD *type,
711                                 char *buf)
712 {
713         unsigned char md[EVP_MAX_MD_SIZE];
714         unsigned int i, n;
715
716         if (!X509_digest(cert, type, md, &n))
717                 return -ENOMEM;
718
719         for (i=0; i < n; i++)
720                 sprintf(&buf[i*2], "%02X", md[i]);
721
722         return 0;
723 }
724
725 int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
726                              OPENCONNECT_X509 *cert, char *buf)
727 {
728         return get_cert_fingerprint(vpninfo, cert, EVP_md5(), buf);
729 }
730
731 int openconnect_get_cert_sha1(struct openconnect_info *vpninfo,
732                               OPENCONNECT_X509 *cert, char *buf)
733 {
734         return get_cert_fingerprint(vpninfo, cert, EVP_sha1(), buf);
735 }
736
737 static int check_server_cert(struct openconnect_info *vpninfo, X509 *cert)
738 {
739         char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
740         int ret;
741
742         ret = openconnect_get_cert_sha1(vpninfo, cert, fingerprint);
743         if (ret)
744                 return ret;
745
746         if (strcasecmp(vpninfo->servercert, fingerprint)) {
747                 vpn_progress(vpninfo, PRG_ERR,
748                              _("Server SSL certificate didn't match: %s\n"), fingerprint);
749                 return -EINVAL;
750         }
751         return 0;
752 }
753
754 static int match_hostname_elem(const char *hostname, int helem_len,
755                                const char *match, int melem_len)
756 {
757         if (!helem_len && !melem_len)
758                 return 0;
759
760         if (!helem_len || !melem_len)
761                 return -1;
762
763
764         if (match[0] == '*') {
765                 int i;
766
767                 for (i = 1 ; i <= helem_len; i++) {
768                         if (!match_hostname_elem(hostname + i, helem_len - i,
769                                                  match + 1, melem_len - 1))
770                                 return 0;
771                 }
772                 return -1;
773         }
774
775         /* From the NetBSD (5.1) man page for ctype(3):
776            Values of type char or signed char must first be cast to unsigned char,
777            to ensure that the values are within the correct range.  The result
778            should then be cast to int to avoid warnings from some compilers.
779            We do indeed get warning "array subscript has type 'char'" without
780            the casts. Ick. */
781         if (toupper((int)(unsigned char)hostname[0]) ==
782             toupper((int)(unsigned char)match[0]))
783                 return match_hostname_elem(hostname + 1, helem_len - 1,
784                                            match + 1, melem_len - 1);
785
786         return -1;
787 }
788
789 static int match_hostname(const char *hostname, const char *match)
790 {
791         while (*match) {
792                 const char *h_dot, *m_dot;
793                 int helem_len, melem_len;
794
795                 h_dot = strchr(hostname, '.');
796                 m_dot = strchr(match, '.');
797                 
798                 if (h_dot && m_dot) {
799                         helem_len = h_dot - hostname + 1;
800                         melem_len = m_dot - match + 1;
801                 } else if (!h_dot && !m_dot) {
802                         helem_len = strlen(hostname);
803                         melem_len = strlen(match);
804                 } else
805                         return -1;
806
807
808                 if (match_hostname_elem(hostname, helem_len,
809                                         match, melem_len))
810                         return -1;
811
812                 hostname += helem_len;
813                 match += melem_len;
814         }
815         if (*hostname)
816                 return -1;
817
818         return 0;
819 }
820
821 /* cf. RFC2818 and RFC2459 */
822 static int match_cert_hostname(struct openconnect_info *vpninfo, X509 *peer_cert)
823 {
824         STACK_OF(GENERAL_NAME) *altnames;
825         X509_NAME *subjname;
826         ASN1_STRING *subjasn1;
827         char *subjstr = NULL;
828         int addrlen = 0;
829         int i, altdns = 0;
830         char addrbuf[sizeof(struct in6_addr)];
831         int ret;
832
833         /* Allow GEN_IP in the certificate only if we actually connected
834            by IP address rather than by name. */
835         if (inet_pton(AF_INET, vpninfo->hostname, addrbuf) > 0)
836                 addrlen = 4;
837         else if (inet_pton(AF_INET6, vpninfo->hostname, addrbuf) > 0)
838                 addrlen = 16;
839         else if (vpninfo->hostname[0] == '[' &&
840                  vpninfo->hostname[strlen(vpninfo->hostname)-1] == ']') {
841                 char *p = &vpninfo->hostname[strlen(vpninfo->hostname)-1];
842                 *p = 0;
843                 if (inet_pton(AF_INET6, vpninfo->hostname + 1, addrbuf) > 0)
844                         addrlen = 16;
845                 *p = ']';
846         }
847
848         altnames = X509_get_ext_d2i(peer_cert, NID_subject_alt_name,
849                                     NULL, NULL);
850         for (i = 0; i < sk_GENERAL_NAME_num(altnames); i++) {
851                 const GENERAL_NAME *this = sk_GENERAL_NAME_value(altnames, i);
852
853                 if (this->type == GEN_DNS) {
854                         char *str;
855
856                         int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
857                         if (len < 0)
858                                 continue;
859
860                         altdns = 1;
861
862                         /* We don't like names with embedded NUL */
863                         if (strlen(str) != len)
864                                 continue;
865
866                         if (!match_hostname(vpninfo->hostname, str)) {
867                                 vpn_progress(vpninfo, PRG_TRACE,
868                                              _("Matched DNS altname '%s'\n"),
869                                              str);
870                                 GENERAL_NAMES_free(altnames);
871                                 OPENSSL_free(str);
872                                 return 0;
873                         } else {
874                                 vpn_progress(vpninfo, PRG_TRACE,
875                                              _("No match for altname '%s'\n"),
876                                              str);
877                         }
878                         OPENSSL_free(str);
879                 } else if (this->type == GEN_IPADD && addrlen) {
880                         char host[80];
881                         int family;
882
883                         if (this->d.ip->length == 4) {
884                                 family = AF_INET;
885                         } else if (this->d.ip->length == 16) {
886                                 family = AF_INET6;
887                         } else {
888                                 vpn_progress(vpninfo, PRG_ERR,
889                                              _("Certificate has GEN_IPADD altname with bogus length %d\n"),
890                                              this->d.ip->length);
891                                 continue;
892                         }
893                         
894                         /* We only do this for the debug messages */
895                         inet_ntop(family, this->d.ip->data, host, sizeof(host));
896
897                         if (this->d.ip->length == addrlen &&
898                             !memcmp(addrbuf, this->d.ip->data, addrlen)) {
899                                 vpn_progress(vpninfo, PRG_TRACE,
900                                              _("Matched %s address '%s'\n"),
901                                              (family == AF_INET6)?"IPv6":"IPv4",
902                                              host);
903                                 GENERAL_NAMES_free(altnames);
904                                 return 0;
905                         } else {
906                                 vpn_progress(vpninfo, PRG_TRACE,
907                                              _("No match for %s address '%s'\n"),
908                                              (family == AF_INET6)?"IPv6":"IPv4",
909                                              host);
910                         }
911                 } else if (this->type == GEN_URI) {
912                         char *str;
913                         char *url_proto, *url_host, *url_path, *url_host2;
914                         int url_port;
915                         int len = ASN1_STRING_to_UTF8((void *)&str, this->d.ia5);
916
917                         if (len < 0)
918                                 continue;
919
920                         /* We don't like names with embedded NUL */
921                         if (strlen(str) != len)
922                                 continue;
923
924                         if (internal_parse_url(str, &url_proto, &url_host, &url_port, &url_path, 0)) {
925                                 OPENSSL_free(str);
926                                 continue;
927                         }
928
929                         if (!url_proto || strcasecmp(url_proto, "https"))
930                                 goto no_uri_match;
931
932                         if (url_port != vpninfo->port)
933                                 goto no_uri_match;
934
935                         /* Leave url_host as it was so that it can be freed */
936                         url_host2 = url_host;
937                         if (addrlen == 16 && vpninfo->hostname[0] != '[' &&
938                             url_host[0] == '[' && url_host[strlen(url_host)-1] == ']') {
939                                 /* Cope with https://[IPv6]/ when the hostname is bare IPv6 */
940                                 url_host[strlen(url_host)-1] = 0;
941                                 url_host2++;
942                         }
943
944                         if (strcasecmp(vpninfo->hostname, url_host2))
945                                 goto no_uri_match;
946
947                         if (url_path) {
948                                 vpn_progress(vpninfo, PRG_TRACE,
949                                              _("URI '%s' has non-empty path; ignoring\n"),
950                                              str);
951                                 goto no_uri_match_silent;
952                         }
953                         vpn_progress(vpninfo, PRG_TRACE,
954                                      _("Matched URI '%s'\n"),
955                                      str);
956                         free(url_proto);
957                         free(url_host);
958                         free(url_path);
959                         OPENSSL_free(str);
960                         GENERAL_NAMES_free(altnames);
961                         return 0;
962
963                 no_uri_match:
964                         vpn_progress(vpninfo, PRG_TRACE,
965                                      _("No match for URI '%s'\n"),
966                                      str);
967                 no_uri_match_silent:
968                         free(url_proto);
969                         free(url_host);
970                         free(url_path);
971                         OPENSSL_free(str);
972                 }
973         }
974         GENERAL_NAMES_free(altnames);
975
976         /* According to RFC2818, we don't use the legacy subject name if
977            there was an altname with DNS type. */
978         if (altdns) {
979                 vpn_progress(vpninfo, PRG_ERR,
980                              _("No altname in peer cert matched '%s'\n"),
981                              vpninfo->hostname);
982                 return -EINVAL;
983         }
984
985         subjname = X509_get_subject_name(peer_cert);
986         if (!subjname) {
987                 vpn_progress(vpninfo, PRG_ERR,
988                              _("No subject name in peer cert!\n"));
989                 return -EINVAL;
990         }
991
992         /* Find the _last_ (most specific) commonName */
993         i = -1;
994         while (1) {
995                 int j = X509_NAME_get_index_by_NID(subjname, NID_commonName, i);
996                 if (j >= 0)
997                         i = j;
998                 else
999                         break;
1000         }
1001
1002         subjasn1 = X509_NAME_ENTRY_get_data(X509_NAME_get_entry(subjname, i));
1003
1004         i = ASN1_STRING_to_UTF8((void *)&subjstr, subjasn1);
1005
1006         if (!subjstr || strlen(subjstr) != i) {
1007                 vpn_progress(vpninfo, PRG_ERR,
1008                              _("Failed to parse subject name in peer cert\n"));
1009                 return -EINVAL;
1010         }
1011         ret = 0;
1012
1013         if (match_hostname(vpninfo->hostname, subjstr)) {
1014                 vpn_progress(vpninfo, PRG_ERR,
1015                              _("Peer cert subject mismatch ('%s' != '%s')\n"),
1016                              subjstr, vpninfo->hostname);
1017                 ret = -EINVAL;
1018         } else {
1019                 vpn_progress(vpninfo, PRG_TRACE,
1020                              _("Matched peer certificate subject name '%s'\n"),
1021                              subjstr);
1022         }
1023
1024         OPENSSL_free(subjstr);                    
1025         return ret;
1026 }
1027
1028 static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl)
1029 {
1030         X509 *peer_cert;
1031         int ret;
1032
1033         peer_cert = SSL_get_peer_certificate(https_ssl);
1034
1035         if (vpninfo->servercert) {
1036                 /* If given a cert fingerprint on the command line, that's
1037                    all we look for */
1038                 ret = check_server_cert(vpninfo, peer_cert);
1039         } else {
1040                 int vfy = SSL_get_verify_result(https_ssl);
1041                 const char *err_string = NULL;
1042
1043                 if (vfy != X509_V_OK)
1044                         err_string = X509_verify_cert_error_string(vfy);
1045                 else if (match_cert_hostname(vpninfo, peer_cert))
1046                         err_string = _("certificate does not match hostname");
1047
1048                 if (err_string) {
1049                         vpn_progress(vpninfo, PRG_INFO,
1050                                      _("Server certificate verify failed: %s\n"),
1051                                      err_string);
1052
1053                         if (vpninfo->validate_peer_cert)
1054                                 ret = vpninfo->validate_peer_cert(vpninfo->cbdata,
1055                                                                   peer_cert,
1056                                                                   err_string);
1057                         else
1058                                 ret = -EINVAL;
1059                 } else {
1060                         ret = 0;
1061                 }
1062         }
1063         X509_free(peer_cert);
1064
1065         return ret;
1066 }
1067
1068 static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
1069                                              SSL *ssl)
1070 {
1071         /* OpenSSL has problems with certificate chains -- if there are
1072            multiple certs with the same name, it doesn't necessarily
1073            choose the _right_ one. (RT#1942)
1074            Pick the right ones for ourselves and add them manually. */
1075         X509 *cert = SSL_get_certificate(ssl);
1076         X509 *cert2;
1077         X509_STORE *store = SSL_CTX_get_cert_store(vpninfo->https_ctx);
1078         X509_STORE_CTX ctx;
1079
1080         if (!cert || !store)
1081                 return;
1082
1083         /* If we already have 'supporting' certs, don't add them again */
1084         if (vpninfo->https_ctx->extra_certs)
1085                 return;
1086
1087         if (!X509_STORE_CTX_init(&ctx, store, NULL, NULL))
1088                 return;
1089
1090         while (ctx.get_issuer(&cert2, &ctx, cert) == 1) {
1091                 char buf[200];
1092                 if (cert2 == cert)
1093                         break;
1094                 if (X509_check_issued(cert2, cert2) == X509_V_OK)
1095                         break;
1096                 cert = cert2;
1097                 X509_NAME_oneline(X509_get_subject_name(cert),
1098                                   buf, sizeof(buf));
1099                 vpn_progress(vpninfo, PRG_DEBUG,
1100                              _("Extra cert from cafile: '%s'\n"), buf);
1101                 SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert);
1102         }
1103         X509_STORE_CTX_cleanup(&ctx);
1104 }
1105
1106 #if OPENSSL_VERSION_NUMBER >= 0x00908000
1107 static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
1108 {
1109         /* We've seen certificates in the wild which don't have the
1110            purpose fields filled in correctly */
1111         X509_VERIFY_PARAM_set_purpose(ctx->param, X509_PURPOSE_ANY);
1112         return X509_verify_cert(ctx);
1113 }
1114 #endif
1115
1116 static int check_certificate_expiry(struct openconnect_info *vpninfo)
1117 {
1118         ASN1_TIME *notAfter;
1119         const char *reason = NULL;
1120         time_t t;
1121         int i;
1122
1123         if (!vpninfo->cert_x509)
1124                 return 0;
1125
1126         t = time(NULL);
1127         notAfter = X509_get_notAfter(vpninfo->cert_x509);
1128         i = X509_cmp_time(notAfter, &t);
1129         if (!i) {
1130                 vpn_progress(vpninfo, PRG_ERR,
1131                              _("Error in client cert notAfter field\n"));
1132                 return -EINVAL;
1133         } else if (i < 0) {
1134                 reason = _("Client certificate has expired at");
1135         } else {
1136                 t += vpninfo->cert_expire_warning;
1137                 i = X509_cmp_time(notAfter, &t);
1138                 if (i < 0) {
1139                         reason = _("Client certificate expires soon at");
1140                 }
1141         }
1142         if (reason) {
1143                 BIO *bp = BIO_new(BIO_s_mem());
1144                 BUF_MEM *bm;
1145                 const char *expiry = _("<error>");
1146                 char zero = 0;
1147
1148                 if (bp) {
1149                         ASN1_TIME_print(bp, notAfter);
1150                         BIO_write(bp, &zero, 1);
1151                         BIO_get_mem_ptr(bp, &bm);
1152                         expiry = bm->data;
1153                 }
1154                 vpn_progress(vpninfo, PRG_ERR, "%s: %s\n", reason, expiry);
1155                 if (bp)
1156                         BIO_free(bp);
1157         }
1158         return 0;
1159 }
1160 int openconnect_open_https(struct openconnect_info *vpninfo)
1161 {
1162         method_const SSL_METHOD *ssl3_method;
1163         SSL *https_ssl;
1164         BIO *https_bio;
1165         int ssl_sock;
1166         int err;
1167
1168         if (vpninfo->https_ssl)
1169                 return 0;
1170
1171         if (vpninfo->peer_cert) {
1172                 X509_free(vpninfo->peer_cert);
1173                 vpninfo->peer_cert = NULL;
1174         }
1175
1176         ssl_sock = connect_https_socket(vpninfo);
1177         if (ssl_sock < 0)
1178                 return ssl_sock;
1179
1180         ssl3_method = TLSv1_client_method();
1181         if (!vpninfo->https_ctx) {
1182                 vpninfo->https_ctx = SSL_CTX_new(ssl3_method);
1183
1184                 /* Some servers (or their firewalls) really don't like seeing
1185                    extensions. */
1186 #ifdef SSL_OP_NO_TICKET
1187                 SSL_CTX_set_options (vpninfo->https_ctx, SSL_OP_NO_TICKET);
1188 #endif
1189
1190                 if (vpninfo->cert) {
1191                         err = load_certificate(vpninfo);
1192                         if (err) {
1193                                 vpn_progress(vpninfo, PRG_ERR,
1194                                              _("Loading certificate failed. Aborting.\n"));
1195                                 return err;
1196                         }
1197                         check_certificate_expiry(vpninfo);
1198                 }
1199
1200                 /* We just want to do:
1201                    SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY); 
1202                    ... but it doesn't work with OpenSSL < 0.9.8k because of 
1203                    problems with inheritance (fixed in v1.1.4.6 of
1204                    crypto/x509/x509_vpm.c) so we have to play silly buggers
1205                    instead. This trick doesn't work _either_ in < 0.9.7 but
1206                    I don't know of _any_ workaround which will, and can't
1207                    be bothered to find out either. */
1208 #if OPENSSL_VERSION_NUMBER >= 0x00908000
1209                 SSL_CTX_set_cert_verify_callback(vpninfo->https_ctx,
1210                                                  ssl_app_verify_callback, NULL);
1211 #endif
1212                 SSL_CTX_set_default_verify_paths(vpninfo->https_ctx);
1213
1214                 if (vpninfo->cafile) {
1215                         if (!SSL_CTX_load_verify_locations(vpninfo->https_ctx, vpninfo->cafile, NULL)) {
1216                                 vpn_progress(vpninfo, PRG_ERR,
1217                                              _("Failed to open CA file '%s'\n"),
1218                                              vpninfo->cafile);
1219                                 openconnect_report_ssl_errors(vpninfo);
1220                                 close(ssl_sock);
1221                                 return -EINVAL;
1222                         }
1223                 }
1224
1225         }
1226         https_ssl = SSL_new(vpninfo->https_ctx);
1227         workaround_openssl_certchain_bug(vpninfo, https_ssl);
1228
1229         https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE);
1230         BIO_set_nbio(https_bio, 1);
1231         SSL_set_bio(https_ssl, https_bio, https_bio);
1232
1233         vpn_progress(vpninfo, PRG_INFO, _("SSL negotiation with %s\n"),
1234                      vpninfo->hostname);
1235
1236         while ((err = SSL_connect(https_ssl)) <= 0) {
1237                 fd_set wr_set, rd_set;
1238                 int maxfd = ssl_sock;
1239                 
1240                 FD_ZERO(&wr_set);
1241                 FD_ZERO(&rd_set);
1242
1243                 err = SSL_get_error(https_ssl, err);
1244                 if (err == SSL_ERROR_WANT_READ)
1245                         FD_SET(ssl_sock, &rd_set);
1246                 else if (err == SSL_ERROR_WANT_WRITE)
1247                         FD_SET(ssl_sock, &wr_set);
1248                 else {
1249                         vpn_progress(vpninfo, PRG_ERR, _("SSL connection failure\n"));
1250                         openconnect_report_ssl_errors(vpninfo);
1251                         SSL_free(https_ssl);
1252                         close(ssl_sock);
1253                         return -EINVAL;
1254                 }
1255
1256                 if (vpninfo->cancel_fd != -1) {
1257                         FD_SET(vpninfo->cancel_fd, &rd_set);
1258                         if (vpninfo->cancel_fd > ssl_sock)
1259                                 maxfd = vpninfo->cancel_fd;
1260                 }
1261                 select(maxfd + 1, &rd_set, &wr_set, NULL, NULL);
1262                 if (vpninfo->cancel_fd != -1 &&
1263                     FD_ISSET(vpninfo->cancel_fd, &rd_set)) {
1264                         vpn_progress(vpninfo, PRG_ERR, _("SSL connection cancelled\n"));
1265                         SSL_free(https_ssl);
1266                         close(ssl_sock);
1267                         return -EINVAL;
1268                 }
1269         }
1270
1271         if (verify_peer(vpninfo, https_ssl)) {
1272                 SSL_free(https_ssl);
1273                 close(ssl_sock);
1274                 return -EINVAL;
1275         }
1276
1277         vpninfo->ssl_fd = ssl_sock;
1278         vpninfo->https_ssl = https_ssl;
1279
1280         /* Stash this now, because it might not be available later if the
1281            server has disconnected. */
1282         vpninfo->peer_cert = SSL_get_peer_certificate(vpninfo->https_ssl);
1283
1284         vpn_progress(vpninfo, PRG_INFO, _("Connected to HTTPS on %s\n"),
1285                      vpninfo->hostname);
1286
1287         return 0;
1288 }
1289
1290 void openconnect_close_https(struct openconnect_info *vpninfo, int final)
1291 {
1292         if (vpninfo->peer_cert) {
1293                 X509_free(vpninfo->peer_cert);
1294                 vpninfo->peer_cert = NULL;
1295         }
1296         if (vpninfo->https_ssl) {
1297                 SSL_free(vpninfo->https_ssl);
1298                 vpninfo->https_ssl = NULL;
1299         }
1300         if (vpninfo->ssl_fd != -1) {
1301                 close(vpninfo->ssl_fd);
1302                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_rfds);
1303                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
1304                 FD_CLR(vpninfo->ssl_fd, &vpninfo->select_efds);
1305                 vpninfo->ssl_fd = -1;
1306         }
1307         if (final && vpninfo->https_ctx) {
1308                 SSL_CTX_free(vpninfo->https_ctx);
1309                 vpninfo->https_ctx = NULL;
1310         }
1311 }
1312
1313 void openconnect_init_ssl(void)
1314 {
1315         SSL_library_init ();
1316         ERR_clear_error ();
1317         SSL_load_error_strings ();
1318         OpenSSL_add_all_algorithms ();
1319 }
1320
1321 char *openconnect_get_cert_details(struct openconnect_info *vpninfo,
1322                                    OPENCONNECT_X509 *cert)
1323 {
1324         BIO *bp = BIO_new(BIO_s_mem());
1325         BUF_MEM *certinfo;
1326         char zero = 0;
1327         char *ret;
1328
1329         X509_print_ex(bp, cert, 0, 0);
1330         BIO_write(bp, &zero, 1);
1331         BIO_get_mem_ptr(bp, &certinfo);
1332
1333         ret = strdup(certinfo->data);
1334         BIO_free(bp);
1335         return ret;
1336 }
1337
1338
1339 int openconnect_local_cert_md5(struct openconnect_info *vpninfo,
1340                                char *buf)
1341 {
1342         buf[0] = 0;
1343
1344         if (!vpninfo->cert_x509)
1345                 return -EIO;
1346
1347         if (get_cert_md5_fingerprint(vpninfo, vpninfo->cert_x509, buf))
1348                 return -EIO;
1349
1350         return 0;
1351 }