From 497ba428052f1437187778ceb2293c8eaba5893f Mon Sep 17 00:00:00 2001 From: Matthias Andree Date: Sun, 18 Apr 2010 20:22:27 +0200 Subject: [PATCH] Add --sslcertfile option and FETCHMAIL_NO_DEFAULT_X509_PATHS env var, and always load the default X.509 trust stores, unless the latter is set. --- NEWS | 13 +++++++++++++ driver.c | 3 ++- fetchmail.c | 7 +++++-- fetchmail.h | 1 + fetchmail.man | 53 ++++++++++++++++++++++++++++++++++++++++++--------- imap.c | 2 +- options.c | 9 ++++++++- pop3.c | 2 +- rcfile_l.l | 1 + rcfile_y.y | 3 ++- socket.c | 17 ++++++++++++----- socket.h | 2 +- 12 files changed, 91 insertions(+), 22 deletions(-) diff --git a/NEWS b/NEWS index 802309cf..afecaf0c 100644 --- a/NEWS +++ b/NEWS @@ -62,6 +62,14 @@ fetchmail-6.3.17 (not yet released): due to insufficient buffer size allocation. It would then repeatedly reallocate a larger buffer and fail formatting again. See fetchmail-SA-2010-02.txt. +# FEATURES +* Fetchmail now supports a --sslcertfile option to specify a "CA bundle" + file (a file that contains trusted CA certificates). Since these bundled CA + files do not require c_rehash to be run, they are easier to use and immune to + OpenSSL library updates. Also see CHANGES below. +* Fetchmail now supports a FETCHMAIL_NO_DEFAULT_X509_PATHS environment variable + to defeat loading the default SSL CA certificate locations. Also see CHANGES. + # REGRESSION FIX * Fix string handling in rcfile scanner, which caused fetchmail to misparse a run control file in certain circumstances. Fixes BerliOS bug #14257. @@ -77,6 +85,11 @@ fetchmail-6.3.17 (not yet released): are now helpful pointers to --sslcertpath and c_rehash for "unable to get local issuer certificate" and self-signed certificates -- these usually hint to missing root signing CAs in the certs directory. +* Default locations: Fetchmail will now always load the SSL default trusted CA + certificate locations, unless the environmental variable + FETCHMAIL_NO_DEFAULT_X509_PATHS is set and non-empty. Fetchmail used to load + the default locations only if --sslcertpath was not given. + This is a migration aid for systems upgrading to OpenSSL 1.0.0. # DOCUMENTATION * Fix table of global option to read "set softbounce" where there used to be a diff --git a/driver.c b/driver.c index 32cfffb1..68a388d1 100644 --- a/driver.c +++ b/driver.c @@ -1109,7 +1109,8 @@ static int do_session( /* perform initial SSL handshake on open connection */ if (ctl->use_ssl && SSLOpen(mailserver_socket, ctl->sslcert, ctl->sslkey, - ctl->sslproto, ctl->sslcertck, ctl->sslcertpath, + ctl->sslproto, ctl->sslcertck, + ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, ctl->sslcommonname ? ctl->sslcommonname : realhost, ctl->server.pollname, &ctl->remotename) == -1) diff --git a/fetchmail.c b/fetchmail.c index af2a5dda..6d63c714 100644 --- a/fetchmail.c +++ b/fetchmail.c @@ -981,6 +981,7 @@ static void optmerge(struct query *h2, struct query *h1, int force) FLAG_MERGE(sslcert); FLAG_MERGE(sslproto); FLAG_MERGE(sslcertck); + FLAG_MERGE(sslcertfile); FLAG_MERGE(sslcertpath); FLAG_MERGE(sslcommonname); FLAG_MERGE(sslfingerprint); @@ -1667,9 +1668,11 @@ static void dump_params (struct runctl *runp, printf(GT_(" SSL protocol: %s.\n"), ctl->sslproto); if (ctl->sslcertck) { printf(GT_(" SSL server certificate checking enabled.\n")); - if (ctl->sslcertpath != NULL) - printf(GT_(" SSL trusted certificate directory: %s\n"), ctl->sslcertpath); } + if (ctl->sslcertfile != NULL) + printf(GT_(" SSL trusted certificate file: %s\n"), ctl->sslcertfile); + if (ctl->sslcertpath != NULL) + printf(GT_(" SSL trusted certificate directory: %s\n"), ctl->sslcertpath); if (ctl->sslcommonname != NULL) printf(GT_(" SSL server CommonName: %s\n"), ctl->sslcommonname); if (ctl->sslfingerprint != NULL) diff --git a/fetchmail.h b/fetchmail.h index 40ab2eb8..b9e38271 100644 --- a/fetchmail.h +++ b/fetchmail.h @@ -358,6 +358,7 @@ struct query char *sslcert; /* optional SSL certificate file */ char *sslproto; /** force transport protocol (ssl2|ssl3|ssl23|tls1) - if NULL, use ssl23 for SSL and opportunistic tls1 for non-SSL connections. */ + char *sslcertfile; /* Trusted certificate file for checking the server cert */ char *sslcertpath; /* Trusted certificate directory for checking the server cert */ flag sslcertck; /* Strictly check the server cert. */ char *sslcommonname; /* CommonName to expect from server */ diff --git a/fetchmail.man b/fetchmail.man index 1c6344cc..f6c8915f 100644 --- a/fetchmail.man +++ b/fetchmail.man @@ -10,7 +10,7 @@ .\" Load www macros to process .URL requests, this requires groff: .mso www.tmac .\" -.TH fetchmail 1 "fetchmail 6.3.16" "fetchmail" "fetchmail reference manual" +.TH fetchmail 1 "fetchmail 6.3.17-pre1" "fetchmail" "fetchmail reference manual" .SH NAME fetchmail \- fetch mail from a POP, IMAP, ETRN, or ODMR-capable server @@ -495,10 +495,10 @@ to try appropriate protocols depending on context. (Keyword: sslcertck) .br Causes fetchmail to strictly check the server certificate against a set of -local trusted certificates (see the \fBsslcertpath\fP option). If the server -certificate cannot be obtained or is not signed by one of the trusted ones -(directly or indirectly), the SSL connection will fail, regardless of -the \fBsslfingerprint\fP option. +local trusted certificates (see the \fBsslcertfile\fP and \fBsslcertpath\fP +options). If the server certificate cannot be obtained or is not signed by one +of the trusted ones (directly or indirectly), the SSL connection will fail, +regardless of the \fBsslfingerprint\fP option. .IP Note that CRL (certificate revocation lists) are only supported in OpenSSL 0.9.7 and newer! Your system clock should also be reasonably @@ -507,6 +507,18 @@ accurate when using this option. Note that this optional behavior may become default behavior in future fetchmail versions. .TP +.B \-\-sslcertfile +(Keyword: sslcertfile, since v6.3.17) +.br +Sets the file fetchmail uses to look up local certificates. The default is +empty. This can be given in addition to \fB\-\-sslcertpath\fP below, and +certificates specified in \fB\-\-sslcertfile\fP will be processed before those +in \fB\-\-sslcertpath\fP. The option can be used in addition to \fB\-\-sslcertpath\fP. +.IP +Note that fetchmail will always first load the default SSL trusted CA certificates file +unless that is defeated by setting the environment variable +.BR FETCHMAIL_NO_DEFAULT_X509_PATHS . +.TP .B \-\-sslcertpath (Keyword: sslcertpath) .br @@ -516,6 +528,13 @@ expects it - every time you add or modify a certificate in the directory, you need to use the \fBc_rehash\fP tool (which comes with OpenSSL in the tools/ subdirectory). Also, after OpenSSL upgrades, you may need to run \fBc_rehash\fP; particularly when upgrading from 0.9.X to 1.0.0. +.IP +This can be given in addition to \fB\-\-sslcertfile\fP above, which see for +precedence rules. +.IP +Note that fetchmail will also add the default SSL trusted CA certificates directory +first unless defeated by setting the environment variable +.BR FETCHMAIL_NO_DEFAULT_X509_PATHS . .TP .B \-\-sslcommonname (Keyword: sslcommonname; since v6.3.9) @@ -1806,10 +1825,16 @@ ssl \& \& T{ Connect to server over the specified base protocol using SSL encryption T} sslcert \& \& T{ -Specify file for client side public SSL certificate +Specify file for \fBclient side\fP public SSL certificate +T} +sslcertfile \& \& T{ +Specify file with trusted CA certificates +T} +sslcertpath \& \& T{ +Specify c_rehash-ed directory with trusted CA certificates. T} sslkey \& \& T{ -Specify file for client side private SSL key +Specify file for \fBclient side\fP private SSL key T} sslproto \& \& T{ Force ssl protocol for connection @@ -2728,7 +2753,7 @@ lock file to help prevent concurrent runs (root mode, systems without /var/run). .SH ENVIRONMENT .B FETCHMAILUSER: -If the FETCHMAILUSER variable is set, it is used as the name of the +If this environment variable is set, it is used as the name of the calling user (default local name) for purposes such as mailing error notifications. Otherwise, if either the LOGNAME or USER variable is correctly set (e.g. the corresponding UID matches the session user ID) @@ -2738,13 +2763,23 @@ session ID (this elaborate logic is designed to handle the case of multiple names per userid gracefully). .B FETCHMAILHOME: -If the environment variable FETCHMAILHOME is set to a valid and +If this environment variable is set to a valid and existing directory name, fetchmail will read $FETCHMAILHOME/fetchmailrc (the dot is missing in this case), $FETCHMAILHOME/.fetchids and $FETCHMAILHOME/.fetchmail.pid rather than from the user's home directory. The .netrc file is always looked for in the the invoking user's home directory regardless of FETCHMAILHOME's setting. +.B FETCHMAIL_NO_DEFAULT_X509_PATHS +(since v6.3.17): +If this environment variable is set and not empty, fetchmail will NOT load the +default X.509 trusted certificate locations for SSL/TLS CA certificates. +Default (if variable unset or empty): load certificate locations. This is +rarely necessary outside testing. It might be useful in conjunction with +\fB\-\-sslcertfile\fP and \fB\-\-sslcertpath\fP in case there are broken +certificates in the system directories and the user has no administrator +privileges to remedy the problem. + .B HOME_ETC: If the HOME_ETC variable is set, fetchmail will read $HOME_ETC/.fetchmailrc instead of ~/.fetchmailrc. diff --git a/imap.c b/imap.c index 35b3bd07..7c33d8a4 100644 --- a/imap.c +++ b/imap.c @@ -484,7 +484,7 @@ static int imap_getauth(int sock, struct query *ctl, char *greeting) * (see below). */ if (gen_transact(sock, "STARTTLS") == PS_SUCCESS && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck, - ctl->sslcertpath, ctl->sslfingerprint, commonname, + ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, commonname, ctl->server.pollname, &ctl->remotename) != -1) { /* diff --git a/options.c b/options.c index 71590296..d0c7c2ab 100644 --- a/options.c +++ b/options.c @@ -45,6 +45,7 @@ enum { LA_SSLCERT, LA_SSLPROTO, LA_SSLCERTCK, + LA_SSLCERTFILE, LA_SSLCERTPATH, LA_SSLCOMMONNAME, LA_SSLFINGERPRINT, @@ -131,6 +132,7 @@ static const struct option longoptions[] = { {"sslcert", required_argument, (int *) 0, LA_SSLCERT }, {"sslproto", required_argument, (int *) 0, LA_SSLPROTO }, {"sslcertck", no_argument, (int *) 0, LA_SSLCERTCK }, + {"sslcertfile", required_argument, (int *) 0, LA_SSLCERTFILE }, {"sslcertpath", required_argument, (int *) 0, LA_SSLCERTPATH }, {"sslcommonname", required_argument, (int *) 0, LA_SSLCOMMONNAME }, {"sslfingerprint", required_argument, (int *) 0, LA_SSLFINGERPRINT }, @@ -560,6 +562,10 @@ int parsecmdline (int argc /** argument count */, ctl->sslcertck = FLAG_TRUE; break; + case LA_SSLCERTFILE: + ctl->sslcertfile = prependdir(optarg, currentwd); + break; + case LA_SSLCERTPATH: ctl->sslcertpath = prependdir(optarg, currentwd); break; @@ -641,7 +647,8 @@ int parsecmdline (int argc /** argument count */, P(GT_(" --sslkey ssl private key file\n")); P(GT_(" --sslcert ssl client certificate\n")); P(GT_(" --sslcertck do strict server certificate check (recommended)\n")); - P(GT_(" --sslcertpath path to ssl certificates\n")); + P(GT_(" --sslcertfile path to trusted-CA ssl certificate file\n")); + P(GT_(" --sslcertpath path to trusted-CA ssl certificate directory\n")); P(GT_(" --sslcommonname expect this CommonName from server (discouraged)\n")); P(GT_(" --sslfingerprint fingerprint that must match that of the server's cert.\n")); P(GT_(" --sslproto force ssl protocol (SSL2/SSL3/TLS1)\n")); diff --git a/pop3.c b/pop3.c index abd690e8..0cf58da7 100644 --- a/pop3.c +++ b/pop3.c @@ -491,7 +491,7 @@ static int pop3_getauth(int sock, struct query *ctl, char *greeting) * (see below). */ if (gen_transact(sock, "STLS") == PS_SUCCESS && SSLOpen(sock, ctl->sslcert, ctl->sslkey, "tls1", ctl->sslcertck, - ctl->sslcertpath, ctl->sslfingerprint, commonname, + ctl->sslcertfile, ctl->sslcertpath, ctl->sslfingerprint, commonname, ctl->server.pollname, &ctl->remotename) != -1) { /* diff --git a/rcfile_l.l b/rcfile_l.l index 6d64d041..4bb0864b 100644 --- a/rcfile_l.l +++ b/rcfile_l.l @@ -177,6 +177,7 @@ sslkey { return SSLKEY; } sslcert { return SSLCERT; } sslproto { return SSLPROTO; } sslcertck { return SSLCERTCK; } +sslcertfile { return SSLCERTFILE; } sslcertpath { return SSLCERTPATH; } sslcommonname { return SSLCOMMONNAME; } sslfingerprint { return SSLFINGERPRINT; } diff --git a/rcfile_y.y b/rcfile_y.y index 607e6bd3..fe3a59d1 100644 --- a/rcfile_y.y +++ b/rcfile_y.y @@ -77,7 +77,7 @@ extern char * yytext; %token NO KEEP FLUSH LIMITFLUSH FETCHALL REWRITE FORCECR STRIPCR PASS8BITS %token DROPSTATUS DROPDELIVERED %token DNS SERVICE PORT UIDL INTERVAL MIMEDECODE IDLE CHECKALIAS -%token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTPATH SSLCOMMONNAME SSLFINGERPRINT +%token SSL SSLKEY SSLCERT SSLPROTO SSLCERTCK SSLCERTFILE SSLCERTPATH SSLCOMMONNAME SSLFINGERPRINT %token PRINCIPAL ESMTPNAME ESMTPPASSWORD %token TRACEPOLLS @@ -344,6 +344,7 @@ user_option : TO localnames HERE | SSLCERT STRING {current.sslcert = prependdir ($2, rcfiledir); free($2);} | SSLPROTO STRING {current.sslproto = $2;} | SSLCERTCK {current.sslcertck = FLAG_TRUE;} + | SSLCERTFILE STRING {current.sslcertfile = prependdir($2, rcfiledir); free($2);} | SSLCERTPATH STRING {current.sslcertpath = prependdir($2, rcfiledir); free($2);} | SSLCOMMONNAME STRING {current.sslcommonname = $2;} | SSLFINGERPRINT STRING {current.sslfingerprint = $2;} diff --git a/socket.c b/socket.c index 2ef70961..fd42ca4c 100644 --- a/socket.c +++ b/socket.c @@ -857,7 +857,8 @@ static const char *SSLCertGetCN(const char *mycert, * 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 *certpath, +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; @@ -921,10 +922,16 @@ int SSLOpen(int sock, char *mycert, char *mykey, const char *myproto, int certck * we provide the callback for output and possible fingerprint checks. */ SSL_CTX_set_verify(_ctx[sock], SSL_VERIFY_PEER, SSL_nock_verify_callback); } - if (certpath) - SSL_CTX_load_verify_locations(_ctx[sock], NULL, certpath); - else - SSL_CTX_set_default_verify_paths(_ctx[sock]); + + { + char *t = getenv("FETCHMAIL_NO_DEFAULT_X509_PATHS"); + + if (t == NULL || t[0] == '\0') + SSL_CTX_set_default_verify_paths(_ctx[sock]); + } + + if (certpath || cacertfile) + SSL_CTX_load_verify_locations(_ctx[sock], cacertfile, certpath); _ssl_context[sock] = SSL_new(_ctx[sock]); diff --git a/socket.h b/socket.h index b340c4ce..9365f2f8 100644 --- a/socket.h +++ b/socket.h @@ -75,7 +75,7 @@ FIXME: document this int UnixOpen(const char *path); #ifdef SSL_ENABLE -int SSLOpen(int sock, char *mycert, char *mykey, const char *myproto, int certck, char *certpath, +int SSLOpen(int sock, char *mycert, char *mykey, const char *myproto, int certck, char *cacertfile, char *cacertpath, char *fingerprint, char *servercname, char *label, char **remotename); #endif /* SSL_ENABLE */ -- 2.43.2