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