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