/* * socket.c -- socket library functions * * Copyright 1998 - 2004 by Eric S. Raymond. * Copyright 2004 - 2020 by Matthias Andree. * Contributions by Alexander Bluhm, Earl Chew, John Beck. * For license terms, see the file COPYING in this directory. */ #include "config.h" #include "fetchmail.h" #include "tls-aux.h" #include #include #include #include /* isspace() */ #ifdef HAVE_MEMORY_H #include #endif /* HAVE_MEMORY_H */ #include #include #include #include #include #ifdef HAVE_ARPA_INET_H #include #endif #include #if defined(STDC_HEADERS) #include #endif #if defined(HAVE_UNISTD_H) #include #endif #if defined(HAVE_STDARG_H) #include #else #include #endif #if TIME_WITH_SYS_TIME # include # include #else # if HAVE_SYS_TIME_H # include # else # include # endif #endif #include "socket.h" #include "getaddrinfo.h" #include "i18n.h" #include "sdump.h" #include "uid_db.h" /* Defines to allow BeOS and Cygwin to play nice... */ #ifdef __BEOS__ static char peeked; #define fm_close(a) closesocket(a) #define fm_write(a,b,c) send(a,b,c,0) #define fm_peek(a,b,c) recv(a,b,c,0) #define fm_read(a,b,c) recv(a,b,c,0) #else #define fm_close(a) close(a) #define fm_write(a,b,c) write(a,b,c) #define fm_peek(a,b,c) recv(a,b,c, MSG_PEEK) #ifdef __CYGWIN__ #define fm_read(a,b,c) cygwin_read(a,b,c) static ssize_t cygwin_read(int sock, void *buf, size_t count); #else /* ! __CYGWIN__ */ #define fm_read(a,b,c) read(a,b,c) #endif /* __CYGWIN__ */ #endif /* We need to define h_errno only if it is not already */ #ifndef h_errno # if !HAVE_DECL_H_ERRNO extern int h_errno; # endif #endif /* ndef h_errno */ #ifdef HAVE_SOCKETPAIR static char *const *parse_plugin(const char *plugin, const char *host, const char *service) { char **argvec; const char *c, *p; char *cp, *plugin_copy; unsigned int plugin_copy_len; unsigned int plugin_offset = 0, plugin_copy_offset = 0; unsigned int i, vecsiz = 2 * sizeof(char*), host_count = 0, service_count = 0; unsigned int plugin_len = strlen(plugin); unsigned int host_len = strlen(host); unsigned int service_len = strlen(service); for (c = p = plugin; *c; c++) { if (isspace((unsigned char)*c) && !isspace((unsigned char)*p)) vecsiz += sizeof(char*); if (*p == '%' && *c == 'h') host_count++; if (*p == '%' && *c == 'p') service_count++; p = c; } /* we need to discount 2 bytes for each placeholder */ plugin_copy_len = plugin_len + (host_len - 2) * host_count + (service_len - 2) * service_count; plugin_copy = (char *)malloc(plugin_copy_len + 1); if (!plugin_copy) { report(stderr, GT_("fetchmail: malloc failed\n")); return NULL; } while (plugin_offset < plugin_len && plugin_copy_offset < plugin_copy_len) { if ((plugin[plugin_offset] == '%') && (plugin[plugin_offset + 1] == 'h')) { strcpy(plugin_copy + plugin_copy_offset, host); plugin_offset += 2; plugin_copy_offset += host_len; } else if ((plugin[plugin_offset] == '%') && (plugin[plugin_offset + 1] == 'p')) { strcpy(plugin_copy + plugin_copy_offset, service); plugin_offset += 2; plugin_copy_offset += service_len; } else { plugin_copy[plugin_copy_offset] = plugin[plugin_offset]; plugin_offset++; plugin_copy_offset++; } } plugin_copy[plugin_copy_offset] = 0; /* XXX FIXME - is this perhaps a bit too simplistic to chop down the argument strings without any respect to quoting? * better write a generic function that tracks arguments instead... */ argvec = (char **)malloc(vecsiz); if (!argvec) { free(plugin_copy); report(stderr, GT_("fetchmail: malloc failed\n")); return NULL; } memset(argvec, 0, vecsiz); for (p = cp = plugin_copy, i = 0; *cp; cp++) { if ((!isspace((unsigned char)*cp)) && (cp == p ? 1 : isspace((unsigned char)*p))) { argvec[i] = cp; i++; } p = cp; } for (cp = plugin_copy; *cp; cp++) { if (isspace((unsigned char)*cp)) *cp = 0; } return argvec; } static int handle_plugin(const char *host, const char *service, const char *plugin) /* get a socket mediated through a given external command */ { int fds[2]; char *const *argvec; /* * The author of this code, Felix von Leitner , says: * he chose socketpair() instead of pipe() because socketpair creates * bidirectional sockets while allegedly some pipe() implementations don't. */ if (socketpair(AF_UNIX,SOCK_STREAM,0,fds)) { report(stderr, GT_("fetchmail: socketpair failed\n")); return -1; } switch (fork()) { case -1: /* error */ report(stderr, GT_("fetchmail: fork failed\n")); return -1; case 0: /* child */ /* fds[1] is the parent's end; close it for proper EOF ** detection */ (void) close(fds[1]); if ( (dup2(fds[0],0) == -1) || (dup2(fds[0],1) == -1) ) { report(stderr, GT_("dup2 failed\n")); _exit(EXIT_FAILURE); } /* fds[0] is now connected to 0 and 1; close it */ (void) close(fds[0]); if (outlevel >= O_VERBOSE) report(stderr, GT_("running %s (host %s service %s)\n"), plugin, host, service); argvec = parse_plugin(plugin,host,service); if (argvec == NULL) _exit(EXIT_FAILURE); execvp(*argvec, argvec); report(stderr, GT_("execvp(%s) failed\n"), *argvec); _exit(EXIT_FAILURE); break; default: /* parent */ /* NOP */ break; } /* fds[0] is the child's end; close it for proper EOF detection */ (void) close(fds[0]); return fds[1]; } #endif /* HAVE_SOCKETPAIR */ /** Set socket to SO_KEEPALIVE. \return 0 for success. */ static int SockKeepalive(int so
#!/usr/bin/perl -w
# correct-domino-mime-conversion - does it!
# $Id: domino,v 1.1 2004/06/08 03:59:00 rfunk Exp $

use strict;

# Any arguments are expected to be an mda invocation.
if (@ARGV) {
    my $mda = join(' ', @ARGV);
    open(MDA, "| $mda") or die "Can't exec $mda: $!\n";
    select(MDA);
}

# Look for a Boundary declaration in the message header
my $decltag;
while (<STDIN>) {
    print;
    if (/boundary=\"(.*)\"$/i) {
	$decltag = $1;
    } elsif (/^$/) {
	# An empty line marks the end of the headers.
	last;
    }
}

# If we didn't find a Boundary declaration just pipe the rest of the
# message unchanged.
if (!defined $decltag) {
    while (<STDIN>) {
	print;
    }
    exit 0;
}

# Substitute $decltag for every ocurrence of an outer-level boundary
# string found in the body of the message.
my $usedtag;
while (<STDIN>) {
    if (/^--(.*)$/) {
	$usedtag = $1 unless defined $usedtag;
	if ($1 eq $usedtag) {
	    $_ =  "--$decltag\n";
	} elsif ($1 eq "$usedtag--") {
	    $_ = "--$decltag--\n";
	}
    }
    print;
}

=pod

This script can be used to bypass a bug in the Domino-5.0.2b IMAP
service that manifests itself when you use fetchmail as the IMAP
client.  The problem is that fetchmail (differently from other IMAP
clients) fetches messages in two parts, first the headers and then the
body.  It seems that Domino converts the messages from its internal
format into MIME twice.  In doing so, it declared a boundary string in
the messages Content-type header and uses another one to separate the
parts in the body.

This script should be used as a mda option for fetchmail.  As
arguments to it, pass the former mda you used.  I, for example, use the following entry in my .fetchmailrc:

	poll server ... mda "/usr/bin/procmail -d %T";

To use this filter, I changed the above into the following:

	poll server ... mda "/home/gustavo/bin/correct-domino-mime-conversion /usr/bin/procmail -d %T";

If you do not use a mda normally, you can try the following to call sendmail directly:

	poll server ... mda "/home/gustavo/bin/correct-domino-mime-conversion //wherever/is/your/sendmail -oem -f %F %T";

Without argumets this script is a filter that reads from its stdin and
outputs the result into its stdout.

I should mention that this bug seems to be solved in Domino 5.0.3
(http://www.notes.net/46dom.nsf/434e319a66960d8385256857005cd97b/4499e0db6e43732b852568b2006ef7e9?OpenDocument)
but I have not checked it.

Gustavo.
<gustavo@cpqd.com.br>

=cut
ertificate */ if (!_depth0ck) { _depth0ck = 1; } if ((i = X509_NAME_get_text_by_NID(subj, NID_commonName, buf, sizeof(buf))) != -1) { if (_ssl_server_cname != NULL) { char *p1 = buf; char *p2 = _ssl_server_cname; int matched = 0; STACK_OF(GENERAL_NAME) *gens; /* RFC 2595 section 2.4: find a matching name * first find a match among alternative names */ gens = (STACK_OF(GENERAL_NAME) *)X509_get_ext_d2i(x509_cert, NID_subject_alt_name, NULL, NULL); if (gens) { int j, r; for (j = 0, r = sk_GENERAL_NAME_num(gens); j < r; ++j) { const GENERAL_NAME *gn = sk_GENERAL_NAME_value(gens, j); if (gn->type == GEN_DNS) { char *pp1 = (char *)gn->d.ia5->data; char *pp2 = _ssl_server_cname; if (outlevel >= O_VERBOSE) { report(stdout, GT_("Subject Alternative Name: %s\n"), (tt = sdump(pp1, (size_t)gn->d.ia5->length))); xfree(tt); } /* Name contains embedded NUL characters, so we complain. This * is likely a certificate spoofing attack. */ if ((size_t)gn->d.ia5->length != strlen(pp1)) { report(stderr, GT_("Bad certificate: Subject Alternative Name contains NUL, aborting!\n")); sk_GENERAL_NAME_free(gens); return 0; } if (name_match(pp1, pp2)) { matched = 1; } } } GENERAL_NAMES_free(gens); } if (name_match(p1, p2)) { matched = 1; } if (!matched) { if (strict || SSLverbose) { report(stderr, GT_("Server CommonName mismatch: %s != %s\n"), (tt = sdump(buf, i)), _ssl_server_cname ); xfree(tt); } ok_return = 0; } } else if (ok_return) { report(stderr, GT_("Server name not set, could not verify certificate!\n")); if (strict) return (0); } } else { if (outlevel >= O_VERBOSE) report(stdout, GT_("Unknown Server CommonName\n")); if (ok_return && strict) { report(stderr, GT_("Server name not specified in certificate!\n")); return (0); } } /* Print the finger print. Note that on errors, we might print it more than once * normally; we kluge around that by using a global variable. */ if (_check_fp == 1) { unsigned dp; _check_fp = -1; digest_tp = EVP_md5(); if (digest_tp == NULL) { report(stderr, GT_("EVP_md5() failed!\n")); return (0); } if (!X509_digest(x509_cert, digest_tp, digest, &dsz)) { report(stderr, GT_("Out of memory!\n")); return (0); } tp = text; te = text + sizeof(text); for (dp = 0; dp < dsz; dp++) { esz = snprintf(tp, te - tp, dp > 0 ? ":%02X" : "%02X", digest[dp]); if (esz >= (size_t)(te - tp)) { report(stderr, GT_("Digest text buffer too small!\n")); return (0); } tp += esz; } if (outlevel > O_NORMAL) report(stdout, GT_("%s key fingerprint: %s\n"), _server_label, text); if (_check_digest != NULL) { if (strcasecmp(text, _check_digest) == 0) { if (outlevel > O_NORMAL) report(stdout, GT_("%s fingerprints match.\n"), _server_label); } else { report(stderr, GT_("%s fingerprints do not match!\n"), _server_label); return (0); } } /* if (_check_digest != NULL) */ } /* if (_check_fp) */ } /* if (depth == 0 && !_depth0ck) */ if (err != X509_V_OK && err != _prev_err && !(_check_fp != 0 && _check_digest && !strict)) { char *tmp; int did_rep_err = 0; _prev_err = err; report(stderr, GT_("Server certificate verification error: %s\n"), X509_verify_cert_error_string(err)); /* We gave the error code, but maybe we can add some more details for debugging */ switch (err) { /* actually we do not want to lump these together, but * since OpenSSL flipped the meaning of these error * codes in the past, and they do hardly make a * practical difference because servers need not provide * the root signing certificate, we don't bother telling * users the difference: */ case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT_LOCALLY: case X509_V_ERR_UNABLE_TO_GET_ISSUER_CERT: X509_NAME_oneline(issuer, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; report(stderr, GT_("Broken certification chain at: %s\n"), (tmp = sdump(buf, strlen(buf)))); xfree(tmp); report(stderr, GT_( "This could mean that the server did not provide the intermediate CA's certificate(s), " "which is nothing fetchmail could do anything about. For details, " "please see the README.SSL-SERVER document that ships with fetchmail.\n")); did_rep_err = 1; /* FALLTHROUGH */ case X509_V_ERR_DEPTH_ZERO_SELF_SIGNED_CERT: case X509_V_ERR_SELF_SIGNED_CERT_IN_CHAIN: if (!did_rep_err) { X509_NAME_oneline(issuer, buf, sizeof(buf)); buf[sizeof(buf) - 1] = '\0'; report(stderr, GT_("Missing trust anchor certificate: %s\n"), (tmp = sdump(buf, strlen(buf)))); xfree(tmp); } report(stderr, GT_( "This could mean that the root CA's signing certificate is not in the " "trusted CA certificate location, or that c_rehash needs to be run " "on the certificate directory. For details, please " "see the documentation of --sslcertpath and --sslcertfile in the manual page. " "See README.SSL for details.\n")); break; default: break; } } /* * If not in strict checking mode (--sslcertck), override this * and pretend that verification had succeeded. */ _verify_ok &= ok_return; if (!strict) ok_return = 1; return ok_return; } static int SSL_nock_verify_callback( int ok_return, X509_STORE_CTX *ctx ) { return SSL_verify_callback(ok_return, ctx, 0); } static int SSL_ck_verify_callback( int ok_return, X509_STORE_CTX *ctx ) { return SSL_verify_callback(ok_return, ctx, 1); } /* get commonName from certificate set in file. * commonName is stored in buffer namebuffer, limited with namebufferlen */ static const char *SSLCertGetCN(const char *mycert, char *namebuffer, size_t namebufferlen) { const char *ret = NULL; BIO *certBio = NULL; X509 *x509_cert = NULL; X509_NAME *certname = NULL; if (namebuffer && namebufferlen > 0) { namebuffer[0] = 0x00; certBio = BIO_new_file(mycert,"r"); if (certBio) { x509_cert = PEM_read_bio_X509(certBio,NULL,NULL,NULL); BIO_free(certBio); } if (x509_cert) { certname = X509_get_subject_name(x509_cert); if (certname && X509_NAME_get_text_by_NID(certname, NID_commonName, namebuffer, namebufferlen) > 0) ret = namebuffer; X509_free(x509_cert); } } return ret; } #if !defined(OSSL110_API) /* ===== implementation for OpenSSL 1.0.X and LibreSSL ===== */ static int OSSL10X_proto_version_logic(int sock, const char **myproto, int *avoid_ssl_versions) { if (!*myproto) { *myproto = "auto"; } if (!strcasecmp("ssl3", *myproto)) { #if (HAVE_DECL_SSLV3_CLIENT_METHOD > 0) && (0 == OPENSSL_NO_SSL3 + 0) _ctx[sock] = SSL_CTX_new(SSLv3_client_method()); *avoid_ssl_versions &= ~SSL_OP_NO_SSLv3; #else report(stderr, GT_("Your OpenSSL version does not support SSLv3.\n")); return -1; #endif } else if (!strcasecmp("ssl3+", *myproto)) { *avoid_ssl_versions &= ~SSL_OP_NO_SSLv3; *myproto = NULL; } else if (!strcasecmp("tls1", *myproto)) { _ctx[sock] = SSL_CTX_new(TLSv1_client_method()); } else if (!strcasecmp("tls1+", *myproto)) { *myproto = NULL; #if defined(TLS1_1_VERSION) } else if (!strcasecmp("tls1.1", *myproto)) { _ctx[sock] = SSL_CTX_new(TLSv1_1_client_method()); } else if (!strcasecmp("tls1.1+", *myproto)) { *myproto = NULL; *avoid_ssl_versions |= SSL_OP_NO_TLSv1; #else } else if(!strcasecmp("tls1.1",*myproto) || !strcasecmp("tls1.1+", *myproto)) { report(stderr, GT_("Your OpenSSL version does not support TLS v1.1.\n")); return -1; #endif #if defined(TLS1_2_VERSION) } else if (!strcasecmp("tls1.2", *myproto)) { _ctx[sock] = SSL_CTX_new(TLSv1_2_client_method()); } else if (!strcasecmp("tls1.2+", *myproto)) { *myproto = NULL; *avoid_ssl_versions |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1; #else } else if(!strcasecmp("tls1.2",*myproto) || !strcasecmp("tls1.2+", *myproto)) { report(stderr, GT_("Your OpenSSL version does not support TLS v1.2.\n")); return -1; #endif #if defined(TLS1_3_VERSION) } else if (!strcasecmp("tls1.3", *myproto)) { _ctx[sock] = SSL_CTX_new(TLSv1_3_client_method()); } else if (!strcasecmp("tls1.3+", *myproto)) { *myproto = NULL; *avoid_ssl_versions |= SSL_OP_NO_TLSv1 | SSL_OP_NO_TLSv1_1 | SSL_OP_NO_TLSv1_2; #else } else if(!strcasecmp("tls1.3",*myproto) || !strcasecmp("tls1.3+", *myproto)) { report(stderr, GT_("Your OpenSSL version does not support TLS v1.3.\n")); return -1; #endif } else if (!strcasecmp("ssl23", *myproto) || 0 == strcasecmp("auto", *myproto)) { *myproto = NULL; } else { report(stderr, GT_("Invalid SSL protocol '%s' specified, using default autoselect (auto).\n"), *myproto); *myproto = NULL; } return 0; } #define OSSL_proto_version_logic(a,b,c) OSSL10X_proto_version_logic((a),(b),(c)) #else /* ===== implementation for OpenSSL 1.1.0 ===== */ static int OSSL110_proto_version_logic(int sock, const char **myproto, int *avoid_ssl_versions) { /* NOTE - this code MUST NOT set myproto to NULL, else the * SSL_...set_..._proto_version() call becomes ineffective. */ _ctx[sock] = SSL_CTX_new(TLS_client_method()); SSL_CTX_set_min_proto_version(_ctx[sock], TLS1_VERSION); if (!*myproto) { *myproto = "auto"; } if (!strcasecmp("ssl3", *myproto)) { #if (0 == OPENSSL_NO_SSL3 + 0) SSL_CTX_set_min_proto_version(_ctx[sock], SSL3_VERSION); SSL_CTX_set_max_proto_version(_ctx[sock], SSL3_VERSION); *avoid_ssl_versions &= ~SSL_OP_NO_SSLv3; #else report(stderr, GT_("Your OpenSSL version does not support SSLv3.\n")); return -1; #endif } else if (!strcasecmp("ssl3+", *myproto)) { SSL_CTX_set_min_proto_version(_ctx[sock], SSL3_VERSION); *avoid_ssl_versions &= ~SSL_OP_NO_SSLv3; } else if (!strcasecmp("tls1", *myproto)) { SSL_CTX_set_max_proto_version(_ctx[sock], TLS1_VERSION); } else if (!strcasecmp("tls1+", *myproto)) { /* do nothing, min_proto_version is already at TLS1_VERSION */ #if defined(TLS1_1_VERSION) } else if (!strcasecmp("tls1.1", *myproto)) { SSL_CTX_set_min_proto_version(_ctx[sock], TLS1_1_VERSION); SSL_CTX_set_max_proto_version(_ctx[sock], TLS1_1_VERSION); } else if (!strcasecmp("tls1.1+", *myproto)) { SSL_CTX_set_min_proto_version(_ctx[sock], TLS1_1_VERSION); #else } else if(!strcasecmp("tls1.1",*myproto) || !strcasecmp("tls1.1+", *myproto)) { report(stderr, GT_("Your OpenSSL version does not support TLS v1.1.\n")); return -1; #endif #if defined(TLS1_2_VERSION) } else if (!strcasecmp("tls1.2", *myproto)) { SSL_CTX_set_min_proto_version(_ctx[sock], TLS1_2_VERSION); SSL_CTX_set_max_proto_version(_ctx[sock], TLS1_2_VERSION); } else if (!strcasecmp("tls1.2+", *myproto)) { SSL_CTX_set_min_proto_version(_ctx[sock], TLS1_2_VERSION); #else } else if(!strcasecmp("tls1.2",*myproto) || !strcasecmp("tls1.2+", *myproto)) { report(stderr, GT_("Your OpenSSL version does not support TLS v1.2.\n")); return -1; #endif #if defined(TLS1_3_VERSION) } else if (!strcasecmp("tls1.3", *myproto)) { SSL_CTX_set_min_proto_version(_ctx[sock], TLS1_3_VERSION); SSL_CTX_set_max_proto_version(_ctx[sock], TLS1_3_VERSION); } else if (!strcasecmp("tls1.3+", *myproto)) { SSL_CTX_set_min_proto_version(_ctx[sock], TLS1_3_VERSION); #else } else if(!strcasecmp("tls1.3",*myproto) || !strcasecmp("tls1.3+", *myproto)) { report(stderr, GT_("Your OpenSSL version does not support TLS v1.3.\n")); return -1; #endif } else if (!strcasecmp("ssl23", *myproto) || 0 == strcasecmp("auto", *myproto)) { /* do nothing */ } else { /* This should not happen. */ report(stderr, GT_("Invalid SSL protocol '%s' specified, using default autoselect (auto).\n"), *myproto); report(stderr, "fetchmail internal error in OSSL110_proto_version_logic\n"); abort(); } return 0; } #define OSSL_proto_version_logic(a,b,c) OSSL110_proto_version_logic((a),(b),(c)) #endif /* performs initial SSL handshake over the connected socket * uses SSL *ssl global variable, which is currently defined * in this file */ int SSLOpen(int sock, char *mycert, char *mykey, const char *myproto, int certck, char *cacertfile, char *certpath, char *fingerprint, char *servercname, char *label, char **remotename) { struct stat randstat; int i; int avoid_ssl_versions = SSL_OP_NO_SSLv2 | SSL_OP_NO_SSLv3; long sslopts = SSL_OP_ALL | SSL_OP_SINGLE_DH_USE; int ssle_connect = 0; long ver; #ifndef OSSL110_API SSL_load_error_strings(); SSL_library_init(); OpenSSL_add_all_algorithms(); /* see Debian Bug#576430 and manpage */ #endif ver = OpenSSL_version_num(); /* version switch through tls-aux.h */ if (ver < OPENSSL_VERSION_NUMBER) { report(stderr, GT_("Loaded OpenSSL library %#lx older than headers %#lx, refusing to work.\n"), (long)ver, (long)(OPENSSL_VERSION_NUMBER)); return -1; } if (ver > OPENSSL_VERSION_NUMBER && outlevel >= O_VERBOSE) { report(stdout, GT_("Loaded OpenSSL library %#lx newer than headers %#lx, trying to continue.\n"), (long)ver, (long)(OPENSSL_VERSION_NUMBER)); } if (stat("/dev/random", &randstat) && stat("/dev/urandom", &randstat)) { /* Neither /dev/random nor /dev/urandom are present, so add entropy to the SSL PRNG a hard way. */ for (i = 0; i < 10000 && ! RAND_status (); ++i) { char buf[4]; struct timeval tv; gettimeofday (&tv, 0); buf[0] = tv.tv_usec & 0xF; buf[2] = (tv.tv_usec & 0xF0) >> 4; buf[3] = (tv.tv_usec & 0xF00) >> 8; buf[1] = (tv.tv_usec & 0xF000) >> 12; RAND_add (buf, sizeof buf, 0.1); } } if( sock < 0 || (unsigned)sock > FD_SETSIZE ) { report(stderr, GT_("File descriptor out of range for SSL") ); return( -1 ); } /* Make sure a connection referring to an older context is not left */ _ssl_context[sock] = NULL; { int rc = OSSL_proto_version_logic(sock, &myproto, &avoid_ssl_versions); if (rc) return rc; } /* do not combine into an else { } as myproto may be nulled above! */ if (!myproto) { /* SSLv23 is a misnomer and will in fact use the best available protocol, subject to SSL_OP_NO* constraints. */ _ctx[sock] = SSL_CTX_new(SSLv23_client_method()); } if(_ctx[sock] == NULL) { unsigned long ec = ERR_peek_last_error(); ERR_print_errors_fp(stderr); if (ERR_GET_REASON(ec) == SSL_R_NULL_SSL_METHOD_PASSED) { report(stderr, GT_("Note that some distributions disable older protocol versions in weird non-standard ways. Try a newer protocol version.\n")); } return(-1); } { char *tmp = getenv("FETCHMAIL_DISABLE_CBC_IV_COUNTERMEASURE"); if (tmp == NULL || *tmp == '\0' || strspn(tmp, " \t") == strlen(tmp)) sslopts &= ~ SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS; } SSL_CTX_set_options(_ctx[sock], sslopts | avoid_ssl_versions); if (certck) { SSL_CTX_set_verify(_ctx[sock], SSL_VERIFY_PEER, SSL_ck_verify_callback); } else { /* In this case, we do not fail if verification fails. However, * we provide the callback for output and possible fingerprint * checks. */ SSL_CTX_set_verify(_ctx[sock], SSL_VERIFY_PEER, SSL_nock_verify_callback); } /* Check which trusted X.509 CA certificate store(s) to load */ { char *tmp; int want_default_cacerts = 0; /* Load user locations if any is given */ if (certpath || cacertfile) SSL_CTX_load_verify_locations(_ctx[sock], cacertfile, certpath); else want_default_cacerts = 1; tmp = getenv("FETCHMAIL_INCLUDE_DEFAULT_X509_CA_CERTS"); if (want_default_cacerts || (tmp && tmp[0])) { SSL_CTX_set_default_verify_paths(_ctx[sock]); } } _ssl_context[sock] = SSL_new(_ctx[sock]); if(_ssl_context[sock] == NULL) { ERR_print_errors_fp(stderr); SSL_CTX_free(_ctx[sock]); _ctx[sock] = NULL; return(-1); } /* This static is for the verify callback */ _ssl_server_cname = servercname; _server_label = label; _check_fp = 1; _check_digest = fingerprint; _depth0ck = 0; _firstrun = 1; _verify_ok = 1; _prev_err = -1; /* * Support SNI, some servers (googlemail) appear to require it. */ { long r; r = SSL_set_tlsext_host_name(_ssl_context[sock], servercname); if (0 == r) { /* handle error */ report(stderr, GT_("Warning: SSL_set_tlsext_host_name(%p, \"%s\") failed (code %#lx), trying to continue.\n"), (void *)_ssl_context[sock], servercname, r); ERR_print_errors_fp(stderr); } } /* OpenSSL >= 1.0.2: set host name for verification */ /* XXX FIXME: do we need to change the function's signature and pass the akalist to * permit the other hostnames through SSL? */ /* https://wiki.openssl.org/index.php/Hostname_validation */ { int r; X509_VERIFY_PARAM *param = SSL_get0_param(_ssl_context[sock]); X509_VERIFY_PARAM_set_hostflags(param, X509_CHECK_FLAG_NO_PARTIAL_WILDCARDS); if (0 == (r = X509_VERIFY_PARAM_set1_host(param, servercname, strlen(servercname)))) { report(stderr, GT_("Warning: X509_VERIFY_PARAM_set1_host(%p, \"%s\") failed (code %#x), trying to continue.\n"), (void *)_ssl_context[sock], servercname, r); ERR_print_errors_fp(stderr); } } if( mycert || mykey ) { /* Ok... He has a certificate file defined, so lets declare it. If * he does NOT have a separate certificate and private key file then * assume that it's a combined key and certificate file. */ char buffer[256]; if( !mykey ) mykey = mycert; if( !mycert ) mycert = mykey; if ((!*remotename || !**remotename) && SSLCertGetCN(mycert, buffer, sizeof(buffer))) { free(*remotename); *remotename = xstrdup(buffer); } SSL_use_certificate_file(_ssl_context[sock], mycert, SSL_FILETYPE_PEM); SSL_use_RSAPrivateKey_file(_ssl_context[sock], mykey, SSL_FILETYPE_PEM); } if (SSL_set_fd(_ssl_context[sock], sock) == 0 || (ssle_connect = SSL_connect(_ssl_context[sock])) < 1) { int e = errno; unsigned long ssle_err_from_get_error = SSL_get_error(_ssl_context[sock], ssle_connect); unsigned long ssle_err_from_queue = ERR_peek_error(); ERR_print_errors_fp(stderr); if (SSL_ERROR_SYSCALL == ssle_err_from_get_error && 0 == ssle_err_from_queue) { if (0 == ssle_connect) { /* FIXME: the next line was hacked in 6.4.0-rc1 so the translation strings don't change. * The %s could be merged to the inside of GT_(). */ report(stderr, "%s: %s", servercname, GT_("Server shut down connection prematurely during SSL_connect().\n")); } else if (ssle_connect < 0) { report(stderr, "%s: ", servercname); report(stderr, GT_("System error during SSL_connect(): %s\n"), e ? strerror(e) : GT_("handshake failed at protocol or connection level.")); } } SSL_free( _ssl_context[sock] ); _ssl_context[sock] = NULL; SSL_CTX_free(_ctx[sock]); _ctx[sock] = NULL; return(-1); } if (outlevel >= O_VERBOSE) { SSL_CIPHER const *sc; int bitsmax, bitsused; const char *vers; vers = SSL_get_version(_ssl_context[sock]); sc = SSL_get_current_cipher(_ssl_context[sock]); if (!sc) { report (stderr, GT_("Cannot obtain current SSL/TLS cipher - no session established?\n")); } else { bitsused = SSL_CIPHER_get_bits(sc, &bitsmax); report(stdout, GT_("SSL/TLS: using protocol %s, cipher %s, %d/%d secret/processed bits\n"), vers, SSL_CIPHER_get_name(sc), bitsused, bitsmax); } } /* Paranoia: was the callback not called as we expected? */ if (!_depth0ck) { report(stderr, GT_("Certificate/fingerprint verification was somehow skipped!\n")); if (fingerprint != NULL || certck) { if( NULL != SSLGetContext( sock ) ) { /* Clean up the SSL stack */ SSL_shutdown( _ssl_context[sock] ); SSL_free( _ssl_context[sock] ); _ssl_context[sock] = NULL; SSL_CTX_free(_ctx[sock]); _ctx[sock] = NULL; } return(-1); } } if (!certck && !fingerprint && (SSL_get_verify_result(_ssl_context[sock]) != X509_V_OK || !_verify_ok)) { report(stderr, GT_("Warning: the connection is insecure, continuing anyways. (Better use --sslcertck!)\n")); } return(0); } #endif int SockClose(int sock) /* close a socket gracefully */ { #ifdef SSL_ENABLE if( NULL != SSLGetContext( sock ) ) { /* Clean up the SSL stack */ SSL_shutdown( _ssl_context[sock] ); SSL_free( _ssl_context[sock] ); _ssl_context[sock] = NULL; SSL_CTX_free(_ctx[sock]); _ctx[sock] = NULL; } #endif /* if there's an error closing at this point, not much we can do */ return(fm_close(sock)); /* this is guarded */ } #ifdef __CYGWIN__ /* * Workaround Microsoft Winsock recv/WSARecv(..., MSG_PEEK) bug. * See http://sources.redhat.com/ml/cygwin/2001-08/msg00628.html * for more details. */ static ssize_t cygwin_read(int sock, void *buf, size_t count) { char *bp = (char *)buf; size_t n = 0; if ((n = read(sock, bp, count)) == (size_t)-1) return(-1); if (n != count) { size_t n2 = 0; if (outlevel >= O_VERBOSE) report(stdout, GT_("Cygwin socket read retry\n")); n2 = read(sock, bp + n, count - n); if (n2 == (size_t)-1 || n + n2 != count) { report(stderr, GT_("Cygwin socket read retry failed!\n")); return(-1); } } return count; } #endif /* __CYGWIN__ */