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