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