Work around OpenSSL SEGV when retrying PKCS#12 passphrase
[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 #define _GNU_SOURCE
26 #include <sys/types.h>
27 #include <sys/socket.h>
28 #include <netdb.h>
29 #include <unistd.h>
30 #include <fcntl.h>
31 #include <string.h>
32 #include <ctype.h>
33 #include <stdio.h>
34 #include <netinet/in.h>
35 #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 #endif
43
44 #include <openssl/ssl.h>
45 #include <openssl/err.h>
46 #include <openssl/engine.h>
47 #include <openssl/evp.h>
48 #include <openssl/pkcs12.h>
49 #include <openssl/x509v3.h>
50
51 #include "openconnect.h"
52
53 /* OSX < 1.6 doesn't have AI_NUMERICSERV */
54 #ifndef AI_NUMERICSERV
55 #define AI_NUMERICSERV 0
56 #endif
57
58 /* Helper functions for reading/writing lines over SSL.
59    We could use cURL for the HTTP stuff, but it's overkill */
60
61 int  __attribute__ ((format (printf, 2, 3)))
62         openconnect_SSL_printf(SSL *ssl, const char *fmt, ...)
63 {
64         char buf[1024];
65         va_list args;
66
67         buf[1023] = 0;
68
69         va_start(args, fmt);
70         vsnprintf(buf, 1023, fmt, args);
71         va_end(args);
72         return SSL_write(ssl, buf, strlen(buf));
73
74 }
75
76 static int print_err(const char *str, size_t len, void *ptr)
77 {
78         struct openconnect_info *vpninfo = ptr;
79
80         vpninfo->progress(vpninfo, PRG_ERR, "%s", str);
81         return 0;
82 }
83
84 void report_ssl_errors(struct openconnect_info *vpninfo)
85 {
86         ERR_print_errors_cb(print_err, vpninfo);
87 }
88
89 int openconnect_SSL_gets(SSL *ssl, char *buf, size_t len)
90 {
91         int i = 0;
92         int ret;
93
94         if (len < 2)
95                 return -EINVAL;
96
97         while ( (ret = SSL_read(ssl, buf + i, 1)) == 1) {
98                 if (buf[i] == '\n') {
99                         buf[i] = 0;
100                         if (i && buf[i-1] == '\r') {
101                                 buf[i-1] = 0;
102                                 i--;
103                         }
104                         return i;
105                 }
106                 i++;
107
108                 if (i >= len - 1) {
109                         buf[i] = 0;
110                         return i;
111                 }
112         }
113         if (ret == 0) {
114                 ret = -SSL_get_error(ssl, ret);
115         }
116         buf[i] = 0;
117         return i ?: ret;
118 }
119
120 static int pem_pw_cb(char *buf, int len, int w, void *v)
121 {
122         struct openconnect_info *vpninfo = v;
123
124         /* Only try the provided password once... */
125         SSL_CTX_set_default_passwd_cb(vpninfo->https_ctx, NULL);
126         SSL_CTX_set_default_passwd_cb_userdata(vpninfo->https_ctx, NULL);
127
128         if (len <= strlen(vpninfo->cert_password)) {
129                 vpninfo->progress(vpninfo, PRG_ERR,
130                                   "PEM password too long (%zd >= %d)\n",
131                                   strlen(vpninfo->cert_password), len);
132                 return -1;
133         }
134         strcpy(buf, vpninfo->cert_password);
135         return strlen(vpninfo->cert_password);
136 }
137
138 static int load_pkcs12_certificate(struct openconnect_info *vpninfo, PKCS12 *p12)
139 {
140         EVP_PKEY *pkey = NULL;
141         X509 *cert = NULL;
142         STACK_OF(X509) *ca = sk_X509_new_null();
143         int ret = 0;
144         char pass[PEM_BUFSIZE];
145
146  retrypass:
147         if (!vpninfo->cert_password) {
148                 if (EVP_read_pw_string(pass, PEM_BUFSIZE,
149                                        "Enter PKCS#12 pass phrase:", 0))
150                         return -EINVAL;
151         }
152         if (!PKCS12_parse(p12, vpninfo->cert_password?:pass, &pkey, &cert, &ca)) {
153                 unsigned long err = ERR_peek_error();
154
155                 report_ssl_errors(vpninfo);
156
157                 if (ERR_GET_LIB(err) == ERR_LIB_PKCS12 &&
158                     ERR_GET_FUNC(err) == PKCS12_F_PKCS12_PARSE &&
159                     ERR_GET_REASON(err) == PKCS12_R_MAC_VERIFY_FAILURE) {
160                         vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed (wrong passphrase?)\n");
161                         vpninfo->cert_password = NULL;
162 #if OPENSSL_VERSION_NUMBER < 0x10000002
163                         /* Older versions of OpenSSL screw the ca stack up
164                            and will SEGV if you attempt to free (or reuse) it.
165                            So allocate a new one, and live with the memory leak. */
166                         ca = sk_X509_new_null();
167 #endif
168                         goto retrypass;
169                 }
170
171                 vpninfo->progress(vpninfo, PRG_ERR, "Parse PKCS#12 failed (see above errors)\n");
172                 PKCS12_free(p12);
173                 return -EINVAL;
174         }
175         if (cert) {
176                 SSL_CTX_use_certificate(vpninfo->https_ctx, cert);
177                 X509_free(cert);
178         } else {
179                 vpninfo->progress(vpninfo, PRG_ERR,
180                                   "PKCS#12 contained no certificate!");
181                 ret = -EINVAL;
182         }
183
184         if (pkey) {
185                 SSL_CTX_use_PrivateKey(vpninfo->https_ctx, pkey);
186                 EVP_PKEY_free(pkey);
187         } else {
188                 vpninfo->progress(vpninfo, PRG_ERR,
189                                   "PKCS#12 contained no private key!");
190                 ret = -EINVAL;
191         }
192
193         /* Only include supporting certificates which are actually necessary */
194         if (ca) {
195                 int i;
196         next:
197                 for (i = 0; i < sk_X509_num(ca); i++) {
198                         X509 *cert2 = sk_X509_value(ca, i);
199                         if (X509_check_issued(cert2, cert) == X509_V_OK) {
200                                 char buf[200];
201
202                                 if (cert2 == cert)
203                                         break;
204
205                                 X509_NAME_oneline(X509_get_subject_name(cert2),
206                                                   buf, sizeof(buf));
207                                 vpninfo->progress(vpninfo, PRG_DEBUG,
208                                                   "Extra cert from PKCS#12: '%s'\n", buf);
209                                 SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert2);
210                                 cert = cert2;
211                                 goto next;
212                         }
213                 }
214                 sk_X509_free(ca);
215         }
216
217         PKCS12_free(p12);
218         return ret;
219 }
220
221 static int load_tpm_certificate(struct openconnect_info *vpninfo)
222 {
223         ENGINE *e;
224         EVP_PKEY *key;
225         ENGINE_load_builtin_engines();
226
227         e = ENGINE_by_id("tpm");
228         if (!e) {
229                 vpninfo->progress(vpninfo, PRG_ERR, "Can't load TPM engine.\n");
230                 report_ssl_errors(vpninfo);
231                 return -EINVAL;
232         }
233         if (!ENGINE_init(e) || !ENGINE_set_default_RSA(e) ||
234             !ENGINE_set_default_RAND(e)) {
235                 vpninfo->progress(vpninfo, PRG_ERR, "Failed to init TPM engine\n");
236                 report_ssl_errors(vpninfo);
237                 ENGINE_free(e);
238                 return -EINVAL;
239         }
240
241         if (vpninfo->cert_password) {
242                 if (!ENGINE_ctrl_cmd(e, "PIN", strlen(vpninfo->cert_password),
243                                      vpninfo->cert_password, NULL, 0)) {
244                         vpninfo->progress(vpninfo, PRG_ERR, "Failed to set TPM SRK password\n");
245                         report_ssl_errors(vpninfo);
246                 }
247         }
248         key = ENGINE_load_private_key(e, vpninfo->sslkey, NULL, NULL);
249         if (!key) {
250                 vpninfo->progress(vpninfo, PRG_ERR,
251                                   "Failed to load TPM private key\n");
252                 report_ssl_errors(vpninfo);
253                 ENGINE_free(e);
254                 ENGINE_finish(e);
255                 return -EINVAL;
256         }
257         if (!SSL_CTX_use_PrivateKey(vpninfo->https_ctx, key)) {
258                 vpninfo->progress(vpninfo, PRG_ERR, "Add key from TPM failed\n");
259                 report_ssl_errors(vpninfo);
260                 ENGINE_free(e);
261                 ENGINE_finish(e);
262                 return -EINVAL;
263         }
264         return 0;
265 }
266
267 static int load_certificate(struct openconnect_info *vpninfo)
268 {
269         vpninfo->progress(vpninfo, PRG_TRACE,
270                           "Using certificate file %s\n", vpninfo->cert);
271
272         if (vpninfo->cert_type == CERT_TYPE_PKCS12 ||
273             vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
274                 FILE *f;
275                 PKCS12 *p12;
276
277                 f = fopen(vpninfo->cert, "r");
278                 if (!f) {
279                         vpninfo->progress(vpninfo, PRG_ERR,
280                                           "Failed to open certificate file %s: %s\n",
281                                           vpninfo->cert, strerror(errno));
282                         return -ENOENT;
283                 }
284                 p12 = d2i_PKCS12_fp(f, NULL);
285                 fclose(f);
286                 if (p12)
287                         return load_pkcs12_certificate(vpninfo, p12);
288
289                 /* Not PKCS#12 */
290                 if (vpninfo->cert_type == CERT_TYPE_PKCS12) {
291                         vpninfo->progress(vpninfo, PRG_ERR, "Read PKCS#12 failed\n");
292                         report_ssl_errors(vpninfo);
293                         return -EINVAL;
294                 }
295                 /* Clear error and fall through to see if it's a PEM file... */
296                 ERR_clear_error();
297         }
298
299         /* It's PEM or TPM now, and either way we need to load the plain cert: */
300         if (!SSL_CTX_use_certificate_chain_file(vpninfo->https_ctx,
301                                                 vpninfo->cert)) {
302                 vpninfo->progress(vpninfo, PRG_ERR,
303                                   "Loading certificate failed\n");
304                 report_ssl_errors(vpninfo);
305                 return -EINVAL;
306         }
307
308         if (vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
309                 FILE *f = fopen(vpninfo->sslkey, "r");
310                 char buf[256];
311
312                 if (!f) {
313                         vpninfo->progress(vpninfo, PRG_ERR,
314                                           "Failed to open private key file %s: %s\n",
315                                           vpninfo->cert, strerror(errno));
316                         return -ENOENT;
317                 }
318
319                 buf[255] = 0;
320                 while (fgets(buf, 255, f)) {
321                         if (!strcmp(buf, "-----BEGIN TSS KEY BLOB-----\n")) {
322                                 vpninfo->cert_type = CERT_TYPE_TPM;
323                                 break;
324                         } else if (!strcmp(buf, "-----BEGIN RSA PRIVATE KEY-----\n") ||
325                                    !strcmp(buf, "-----BEGIN DSA PRIVATE KEY-----\n") ||
326                                    !strcmp(buf, "-----BEGIN ENCRYPTED PRIVATE KEY-----\n")) {
327                                 vpninfo->cert_type = CERT_TYPE_PEM;
328                                 break;
329                         }
330                 }
331                 fclose(f);
332                 if (vpninfo->cert_type == CERT_TYPE_UNKNOWN) {
333                         vpninfo->progress(vpninfo, PRG_ERR,
334                                           "Failed to identify private key type in '%s'\n",
335                                           vpninfo->sslkey);
336                         return -EINVAL;
337                 }
338         }
339
340         if (vpninfo->cert_type == CERT_TYPE_TPM)
341                 return load_tpm_certificate(vpninfo);
342
343         /* Standard PEM certificate */
344         if (vpninfo->cert_password) {
345                 SSL_CTX_set_default_passwd_cb(vpninfo->https_ctx,
346                                               pem_pw_cb);
347                 SSL_CTX_set_default_passwd_cb_userdata(vpninfo->https_ctx,
348                                                        vpninfo);
349         }
350  again:
351         if (!SSL_CTX_use_RSAPrivateKey_file(vpninfo->https_ctx, vpninfo->sslkey,
352                                             SSL_FILETYPE_PEM)) {
353                 unsigned long err = ERR_peek_error();
354                 
355                 report_ssl_errors(vpninfo);
356
357 #ifndef EVP_F_EVP_DECRYPTFINAL_EX
358 #define EVP_F_EVP_DECRYPTFINAL_EX EVP_F_EVP_DECRYPTFINAL
359 #endif
360                 /* If the user fat-fingered the passphrase, try again */
361                 if (ERR_GET_LIB(err) == ERR_LIB_EVP &&
362                     ERR_GET_FUNC(err) == EVP_F_EVP_DECRYPTFINAL_EX &&
363                     ERR_GET_REASON(err) == EVP_R_BAD_DECRYPT) {
364                         vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed (wrong passphrase?)\n");
365                         goto again;
366                 }
367                 
368                 vpninfo->progress(vpninfo, PRG_ERR, "Loading private key failed (see above errors)\n");
369                 return -EINVAL;
370         }
371         return 0;
372 }
373
374 enum cert_hash_type {
375         EVP_MD5,
376         EVP_SHA1
377 };
378
379 static int get_cert_fingerprint(struct openconnect_info *vpninfo,
380                                 X509 *cert, enum cert_hash_type hash,
381                                 char *buf)
382 {
383         unsigned char md[EVP_MAX_MD_SIZE];
384         unsigned int i, n;
385
386         switch (hash) {
387         case EVP_MD5:
388                 if (!X509_digest(cert, EVP_md5(), md, &n))
389                         return -ENOMEM;
390                 break;
391         case EVP_SHA1:
392                 if (!X509_digest(cert, EVP_sha1(), md, &n))
393                         return -ENOMEM;
394                 break;
395         default:
396                 vpninfo->progress(vpninfo, PRG_ERR,
397                                   "Unsupported SSL certificate hash function type\n");
398         }
399
400         for (i=0; i < n; i++) {
401                 sprintf(&buf[i*2], "%02X", md[i]);
402         }
403         return 0;
404 }
405
406 int get_cert_md5_fingerprint(struct openconnect_info *vpninfo,
407                              X509 *cert, char *buf)
408 {
409         return get_cert_fingerprint(vpninfo, cert, EVP_MD5, buf);
410 }
411
412 int get_cert_sha1_fingerprint(struct openconnect_info *vpninfo,
413                               X509 *cert, char *buf)
414 {
415         return get_cert_fingerprint(vpninfo, cert, EVP_SHA1, buf);
416 }
417
418 static int check_server_cert(struct openconnect_info *vpninfo, X509 *cert)
419 {
420         char fingerprint[EVP_MAX_MD_SIZE * 2 + 1];
421         int ret;
422
423         ret = get_cert_sha1_fingerprint(vpninfo, cert, fingerprint);
424         if (ret)
425                 return ret;
426
427         if (strcasecmp(vpninfo->servercert, fingerprint)) {
428                 vpninfo->progress(vpninfo, PRG_ERR,
429                                   "Server SSL certificate didn't match: %s\n", fingerprint);
430                 return -EINVAL;
431         }
432         return 0;
433 }
434
435 static int verify_peer(struct openconnect_info *vpninfo, SSL *https_ssl)
436 {
437         X509 *peer_cert;
438
439         if (vpninfo->cafile) {
440                 int vfy = SSL_get_verify_result(https_ssl);
441
442                 if (vfy != X509_V_OK) {
443                         vpninfo->progress(vpninfo, PRG_ERR, "Server certificate verify failed: %s\n",
444                                 X509_verify_cert_error_string(vfy));
445                         return -EINVAL;
446                 }
447                 return 0;
448         }
449
450         peer_cert = SSL_get_peer_certificate(https_ssl);
451
452         if (vpninfo->servercert)
453                 return check_server_cert(vpninfo, peer_cert);
454
455         if (vpninfo->validate_peer_cert)
456                 return vpninfo->validate_peer_cert(vpninfo, peer_cert);
457
458         /* If no validation function, just let it succeed */
459         return 0;
460 }
461
462 static void workaround_openssl_certchain_bug(struct openconnect_info *vpninfo,
463                                              SSL *ssl)
464 {
465         /* OpenSSL has problems with certificate chains -- if there are
466            multiple certs with the same name, it doesn't necessarily
467            choose the _right_ one. (RT#1942)
468            Pick the right ones for ourselves and add them manually. */
469         X509 *cert = SSL_get_certificate(ssl);
470         X509 *cert2;
471         X509_STORE *store = SSL_CTX_get_cert_store(vpninfo->https_ctx);
472         X509_STORE_CTX ctx;
473
474         if (!cert || !store)
475                 return;
476
477         /* If we already have 'supporting' certs, don't add them again */
478         if (vpninfo->https_ctx->extra_certs)
479                 return;
480
481         if (!X509_STORE_CTX_init(&ctx, store, NULL, NULL))
482                 return;
483
484         while (ctx.get_issuer(&cert2, &ctx, cert) == 1) {
485                 char buf[200];
486                 if (cert2 == cert)
487                         break;
488                 cert = cert2;
489                 X509_NAME_oneline(X509_get_subject_name(cert),
490                                   buf, sizeof(buf));
491                 vpninfo->progress(vpninfo, PRG_DEBUG,
492                                   "Extra cert from cafile: '%s'\n", buf);
493                 SSL_CTX_add_extra_chain_cert(vpninfo->https_ctx, cert);
494         }
495         X509_STORE_CTX_cleanup(&ctx);
496 }
497
498 #if OPENSSL_VERSION_NUMBER >= 0x00908000
499 static int ssl_app_verify_callback(X509_STORE_CTX *ctx, void *arg)
500 {
501         /* We've seen certificates in the wild which don't have the
502            purpose fields filled in correctly */
503         X509_VERIFY_PARAM_set_purpose(ctx->param, X509_PURPOSE_ANY);
504         return X509_verify_cert(ctx);
505 }
506 #endif
507
508 int openconnect_open_https(struct openconnect_info *vpninfo)
509 {
510         method_const SSL_METHOD *ssl3_method;
511         SSL *https_ssl;
512         BIO *https_bio;
513         int ssl_sock = -1;
514         int err;
515
516         if (!vpninfo->port)
517                 vpninfo->port = 443;
518
519         if (vpninfo->peer_addr) {
520                 ssl_sock = socket(vpninfo->peer_addr->sa_family, SOCK_STREAM, IPPROTO_IP);
521                 if (ssl_sock < 0) {
522                 reconn_err:
523                         vpninfo->progress(vpninfo, PRG_ERR, "Failed to reconnect to %s %s\n",
524                                           vpninfo->proxy?"proxy":"host",
525                                           vpninfo->proxy?:vpninfo->hostname);
526                         return -EINVAL;
527                 }
528                 if (connect(ssl_sock, vpninfo->peer_addr, vpninfo->peer_addrlen))
529                         goto reconn_err;
530                 
531         } else {
532                 struct addrinfo hints, *result, *rp;
533                 char *hostname;
534                 char port[6];
535
536                 memset(&hints, 0, sizeof(struct addrinfo));
537                 hints.ai_family = AF_UNSPEC;
538                 hints.ai_socktype = SOCK_STREAM;
539                 hints.ai_flags = AI_PASSIVE | AI_NUMERICSERV;
540                 hints.ai_protocol = 0;
541                 hints.ai_canonname = NULL;
542                 hints.ai_addr = NULL;
543                 hints.ai_next = NULL;
544
545                 /* The 'port' variable is a string because it's easier
546                    this way than if we pass NULL to getaddrinfo() and
547                    then try to fill in the numeric value into
548                    different types of returned sockaddr_in{6,}. */
549 #ifdef OPENCONNECT_LIBPROXY
550                 if (vpninfo->proxy_factory) {
551                         char *url;
552                         char **proxies;
553                         int i = 0;
554
555                         free(vpninfo->proxy_type);
556                         vpninfo->proxy_type = NULL;
557                         free(vpninfo->proxy);
558                         vpninfo->proxy = NULL;
559
560                         if (vpninfo->port == 443)
561                                 i = asprintf(&url, "https://%s/%s", vpninfo->hostname,
562                                              vpninfo->urlpath?:"");
563                         else
564                                 i = asprintf(&url, "https://%s:%d/%s", vpninfo->hostname,
565                                              vpninfo->port, vpninfo->urlpath?:"");
566                         if (i == -1)
567                                 return -ENOMEM;
568
569                         proxies = px_proxy_factory_get_proxies(vpninfo->proxy_factory,
570                                                                url);
571
572                         while (proxies && proxies[i]) {
573                                 if (!vpninfo->proxy &&
574                                     (!strncmp(proxies[i], "http://", 7) ||
575                                      !strncmp(proxies[i], "socks://", 8) ||
576                                      !strncmp(proxies[i], "socks5://", 9)))
577                                         parse_url(proxies[i], &vpninfo->proxy_type,
578                                                   &vpninfo->proxy, &vpninfo->proxy_port,
579                                                   NULL, 0);
580                                 i++;
581                         }
582                         free(url);
583                         free(proxies);
584                         if (vpninfo->proxy)
585                                 vpninfo->progress(vpninfo, PRG_TRACE, "Proxy from libproxy: %s://%s:%d/\n",
586                                                   vpninfo->proxy_type, vpninfo->proxy, vpninfo->port);
587                 }
588 #endif
589                 if (vpninfo->proxy) {
590                         hostname = vpninfo->proxy;
591                         snprintf(port, 6, "%d", vpninfo->proxy_port);
592                 } else {
593                         hostname = vpninfo->hostname;
594                         snprintf(port, 6, "%d", vpninfo->port);
595                 }
596
597                 if (hostname[0] == '[' && hostname[strlen(hostname)-1] == ']') {
598                         /* Solaris has no strndup(). */
599                         int len = strlen(hostname) - 2;
600                         char *new_hostname = malloc(len + 1);
601                         if (!new_hostname)
602                                 return -ENOMEM;
603                         memcpy(new_hostname, hostname + 1, len);
604                         new_hostname[len] = 0;
605
606                         hostname = new_hostname;
607                         hints.ai_flags |= AI_NUMERICHOST;
608                 }
609
610                 err = getaddrinfo(hostname, port, &hints, &result);
611                 if (hints.ai_flags & AI_NUMERICHOST)
612                         free(hostname);
613
614                 if (err) {
615                         vpninfo->progress(vpninfo, PRG_ERR, "getaddrinfo failed for host '%s': %s\n",
616                                           hostname, gai_strerror(err));
617                         return -EINVAL;
618                 }
619
620                 for (rp = result; rp ; rp = rp->ai_next) {
621                         char host[80];
622
623                         if (!getnameinfo(rp->ai_addr, rp->ai_addrlen, host,
624                                          sizeof(host), NULL, 0, NI_NUMERICHOST))
625                                 vpninfo->progress(vpninfo, PRG_INFO,
626                                                   "Attempting to connect to %s%s%s:%s\n",
627                                                   rp->ai_family == AF_INET6?"[":"",
628                                                   host,
629                                                   rp->ai_family == AF_INET6?"]":"",
630                                                   port);
631                         
632                         ssl_sock = socket(rp->ai_family, rp->ai_socktype,
633                                           rp->ai_protocol);
634                         if (ssl_sock < 0)
635                                 continue;
636                         if (connect(ssl_sock, rp->ai_addr, rp->ai_addrlen) >= 0) {
637                                 /* Store the peer address we actually used, so that DTLS can
638                                    use it again later */
639                                 vpninfo->peer_addr = malloc(rp->ai_addrlen);
640                                 if (!vpninfo->peer_addr) {
641                                         vpninfo->progress(vpninfo, PRG_ERR, "Failed to allocate sockaddr storage\n");
642                                         close(ssl_sock);
643                                         return -ENOMEM;
644                                 }
645                                 vpninfo->peer_addrlen = rp->ai_addrlen;
646                                 memcpy(vpninfo->peer_addr, rp->ai_addr, rp->ai_addrlen);
647                                 break;
648                         }
649                         close(ssl_sock);
650                         ssl_sock = -1;
651                 }
652                 freeaddrinfo(result);
653                 
654                 if (ssl_sock < 0) {
655                         vpninfo->progress(vpninfo, PRG_ERR, "Failed to connect to host %s\n",
656                                           vpninfo->proxy?:vpninfo->hostname);
657                         return -EINVAL;
658                 }
659         }
660         fcntl(ssl_sock, F_SETFD, FD_CLOEXEC);
661
662         if (vpninfo->proxy) {
663                 err = process_proxy(vpninfo, ssl_sock);
664                 if (err) {
665                         close(ssl_sock);
666                         return err;
667                 }
668         }
669
670         ssl3_method = TLSv1_client_method();
671         if (!vpninfo->https_ctx) {
672                 vpninfo->https_ctx = SSL_CTX_new(ssl3_method);
673
674                 if (vpninfo->cert) {
675                         err = load_certificate(vpninfo);
676                         if (err) {
677                                 vpninfo->progress(vpninfo, PRG_ERR,
678                                                   "Loading certificate failed. Aborting.\n");
679                                 return err;
680                         }
681                 }
682
683                 /* We just want to do:
684                    SSL_CTX_set_purpose(vpninfo->https_ctx, X509_PURPOSE_ANY); 
685                    ... but it doesn't work with OpenSSL < 0.9.8k because of 
686                    problems with inheritance (fixed in v1.1.4.6 of
687                    crypto/x509/x509_vpm.c) so we have to play silly buggers
688                    instead. This trick doesn't work _either_ in < 0.9.7 but
689                    I don't know of _any_ workaround which will, and can't
690                    be bothered to find out either. */
691 #if OPENSSL_VERSION_NUMBER >= 0x00908000
692                 SSL_CTX_set_cert_verify_callback(vpninfo->https_ctx,
693                                                  ssl_app_verify_callback, NULL);
694 #endif
695                 SSL_CTX_set_default_verify_paths(vpninfo->https_ctx);
696
697                 if (vpninfo->cafile)
698                         SSL_CTX_load_verify_locations(vpninfo->https_ctx, vpninfo->cafile, NULL);
699
700         }
701         https_ssl = SSL_new(vpninfo->https_ctx);
702         workaround_openssl_certchain_bug(vpninfo, https_ssl);
703
704         https_bio = BIO_new_socket(ssl_sock, BIO_NOCLOSE);
705         SSL_set_bio(https_ssl, https_bio, https_bio);
706
707         vpninfo->progress(vpninfo, PRG_INFO,
708                           "SSL negotiation with %s\n", vpninfo->hostname);
709
710         if (SSL_connect(https_ssl) <= 0) {
711                 vpninfo->progress(vpninfo, PRG_ERR, "SSL connection failure\n");
712                 report_ssl_errors(vpninfo);
713                 SSL_free(https_ssl);
714                 close(ssl_sock);
715                 return -EINVAL;
716         }
717
718         if (verify_peer(vpninfo, https_ssl)) {
719                 SSL_free(https_ssl);
720                 close(ssl_sock);
721                 return -EINVAL;
722         }
723
724         vpninfo->ssl_fd = ssl_sock;
725         vpninfo->https_ssl = https_ssl;
726
727         vpninfo->progress(vpninfo, PRG_INFO,
728                           "Connected to HTTPS on %s\n", vpninfo->hostname);
729
730         return 0;
731 }
732
733 void openconnect_close_https(struct openconnect_info *vpninfo)
734 {
735         SSL_free(vpninfo->https_ssl);
736         vpninfo->https_ssl = NULL;
737         close(vpninfo->ssl_fd);
738         FD_CLR(vpninfo->ssl_fd, &vpninfo->select_rfds);
739         FD_CLR(vpninfo->ssl_fd, &vpninfo->select_wfds);
740         FD_CLR(vpninfo->ssl_fd, &vpninfo->select_efds);
741         vpninfo->ssl_fd = -1;
742 }
743
744 void openconnect_init_openssl(void)
745 {
746         SSL_library_init ();
747         ERR_clear_error ();
748         SSL_load_error_strings ();
749         OpenSSL_add_all_algorithms ();
750 }
751
752 #if defined(__sun__) || defined(__NetBSD__) || defined(__DragonFly__)
753 int passphrase_from_fsid(struct openconnect_info *vpninfo)
754 {
755         struct statvfs buf;
756
757         if (statvfs(vpninfo->sslkey, &buf)) {
758                 int err = errno;
759                 vpninfo->progress(vpninfo, PRG_ERR, "statvfs: %s\n", strerror(errno));
760                 return -err;
761         }
762         if (asprintf(&vpninfo->cert_password, "%lx", buf.f_fsid))
763                 return -ENOMEM;
764         return 0;
765 }
766 #else
767 int passphrase_from_fsid(struct openconnect_info *vpninfo)
768 {
769         struct statfs buf;
770         unsigned *fsid = (unsigned *)&buf.f_fsid;
771         unsigned long long fsid64;
772
773         if (statfs(vpninfo->sslkey, &buf)) {
774                 int err = errno;
775                 vpninfo->progress(vpninfo, PRG_ERR, "statfs: %s\n", strerror(errno));
776                 return -err;
777         }
778         fsid64 = ((unsigned long long)fsid[0] << 32) | fsid[1];
779
780         if (asprintf(&vpninfo->cert_password, "%llx", fsid64))
781                 return -ENOMEM;
782         return 0;
783 }
784 #endif