isync

mailbox synchronization program
git clone https://git.code.sf.net/p/isync/isync
Log | Files | Refs | README | LICENSE

commit 462fed556a4ccb57c1259570583d846d343cc628
parent a310e7e2ba0a6cd7b68729d32a900eece137de8d
Author: Oswald Buddenhagen <ossi@users.sf.net>
Date:   Thu,  3 Oct 2019 20:17:54 +0200

Merge branch '1.3'

Diffstat:
Mconfigure.ac | 6+++---
Msrc/common.h | 1+
Msrc/drv_imap.c | 28++++++++++++++++++----------
Msrc/drv_proxy_gen.pl | 1+
Msrc/mbsync.1 | 15++++++++-------
Msrc/socket.c | 111+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------
6 files changed, 114 insertions(+), 48 deletions(-)

diff --git a/configure.ac b/configure.ac @@ -18,14 +18,14 @@ fi need_perl=5.14 AC_CACHE_CHECK([whether perl is recent enough], ob_cv_perl_ver, [ - if $PERL -e "use v$need_perl;"; then + if $PERL -e "use v$need_perl;" 2> /dev/null; then ob_cv_perl_ver=yes else ob_cv_perl_ver=no fi ]) if test "x$ob_cv_perl_ver" = "xno"; then - AC_MSG_ERROR([perl is too old]) + AC_MSG_ERROR([perl is too old, need v$need_perl]) fi AC_CACHE_CHECK([whether strftime supports %z], ob_cv_strftime_z, @@ -94,7 +94,7 @@ if test "x$ob_cv_with_ssl" != xno; then sav_LDFLAGS=$LDFLAGS LDFLAGS="$LDFLAGS $SSL_LDFLAGS" AC_CHECK_LIB(dl, dlopen, [LIBDL=-ldl]) - AC_CHECK_LIB(crypto, CRYPTO_lock, [LIBCRYPTO=-lcrypto]) + AC_CHECK_LIB(crypto, X509_cmp, [LIBCRYPTO=-lcrypto]) AC_CHECK_LIB(ssl, SSL_connect, [SSL_LIBS="-lssl $LIBCRYPTO $LIBDL" have_ssl_paths=yes]) LDFLAGS=$sav_LDFLAGS diff --git a/src/common.h b/src/common.h @@ -33,6 +33,7 @@ typedef unsigned char uchar; typedef unsigned short ushort; typedef unsigned int uint; +typedef unsigned long ulong; #define as(ar) (sizeof(ar)/sizeof(ar[0])) diff --git a/src/drv_imap.c b/src/drv_imap.c @@ -953,7 +953,7 @@ parse_date( const char *str ) struct tm datetime; memset( &datetime, 0, sizeof(datetime) ); - if (!(end = strptime( str, "%d-%b-%Y %H:%M:%S ", &datetime ))) + if (!(end = strptime( str, "%e-%b-%Y %H:%M:%S ", &datetime ))) return -1; if ((date = timegm( &datetime )) == -1) return -1; @@ -1203,17 +1203,16 @@ parse_response_code( imap_store_t *ctx, imap_cmd_t *cmd, char *s ) return RESP_OK; } +static int parse_list_rsp_p1( imap_store_t *, list_t *, char * ); static int parse_list_rsp_p2( imap_store_t *, list_t *, char * ); static int parse_list_rsp( imap_store_t *ctx, list_t *list, char *cmd ) { - char *arg; list_t *lp; if (!is_list( list )) { free_list( list ); - bad_list: error( "IMAP error: malformed LIST response\n" ); return LIST_BAD; } @@ -1223,10 +1222,19 @@ parse_list_rsp( imap_store_t *ctx, list_t *list, char *cmd ) return LIST_OK; } free_list( list ); - if (!(arg = next_arg( &cmd ))) - goto bad_list; - if (!ctx->delimiter[0]) - ctx->delimiter[0] = arg[0]; + return parse_list( ctx, cmd, parse_list_rsp_p1 ); +} + +static int +parse_list_rsp_p1( imap_store_t *ctx, list_t *list, char *cmd ATTR_UNUSED ) +{ + if (!is_opt_atom( list )) { + error( "IMAP error: malformed LIST response\n" ); + free_list( list ); + return LIST_BAD; + } + if (!ctx->delimiter[0] && is_atom( list )) + ctx->delimiter[0] = list->val[0]; return parse_list( ctx, cmd, parse_list_rsp_p2 ); } @@ -1873,7 +1881,7 @@ ensure_password( imap_server_conf_t *srvc ) if (cmd) { FILE *fp; int ret; - char buffer[80]; + char buffer[2048]; // Hopefully more than enough room for XOAUTH2, etc. tokens if (*cmd == '+') { flushn(); @@ -2079,7 +2087,7 @@ done_sasl_auth( imap_store_t *ctx, imap_cmd_t *cmd ATTR_UNUSED, int response ) int rc = sasl_client_step( ctx->sasl, NULL, 0, &interact, &out, &out_len ); if (process_sasl_step( ctx, rc, NULL, 0, interact, &out, &out_len ) < 0) warn( "Warning: SASL reported failure despite successful IMAP authentication. Ignoring...\n" ); - else if (out) + else if (out_len > 0) warn( "Warning: SASL wants more steps despite successful IMAP authentication. Ignoring...\n" ); } @@ -2180,7 +2188,7 @@ imap_open_store_authenticate2( imap_store_t *ctx ) free( enc ); return; notsasl: - if (!ctx->sasl || sasl_listmech( ctx->sasl, NULL, "", "", "", &saslavail, NULL, NULL ) != SASL_OK) + if (!ctx->sasl || sasl_listmech( ctx->sasl, NULL, "", " ", "", &saslavail, NULL, NULL ) != SASL_OK) saslavail = "(none)"; /* EXTERNAL is always there anyway. */ if (!auth_login) { error( "IMAP error: selected SASL mechanism(s) not available;\n" diff --git a/src/drv_proxy_gen.pl b/src/drv_proxy_gen.pl @@ -109,6 +109,7 @@ sub type_to_format($) { $_ = shift; s/xint /\%\#x/g; + s/uint /\%u/g; s/int /\%d/g; s/const char \*/\%s/g; return $_; diff --git a/src/mbsync.1 b/src/mbsync.1 @@ -267,7 +267,7 @@ with DOS/Windows file systems. .TP \fBSubFolders\fR \fBVerbatim\fR|\fBMaildir++\fR|\fBLegacy\fR The on-disk folder naming style used for hierarchical mailboxes. -This has option has no effect when \fBFlatten\fR is used. +This option has no effect when \fBFlatten\fR is used. .br Suppose mailboxes with the canonical paths \fBtop/sub/subsub\fR and \fBINBOX/sub/subsub\fR, the styles will yield the following on-disk paths: @@ -601,12 +601,13 @@ which in turn are overridden by command line switches. .. .TP \fBSyncState\fR {\fB*\fR|\fIpath\fR} -Set the location of this Channel's synchronization state files. \fB*\fR means -that the state should be saved in a file named .mbsyncstate in the -Slave mailbox itself; this has the advantage that you needn't to care for the -state file if you delete the mailbox, but it works only with Maildir mailboxes, -obviously. Otherwise this is interpreted as a string to prepend to the Slave -mailbox name to make up a complete path. +Set the location of this Channel's synchronization state files. +\fB*\fR means that the state should be saved in a file named .mbsyncstate +in the Slave mailbox itself; this has the advantage that you do not need +to handle the state file separately if you delete the mailbox, but it works +only with Maildir mailboxes, obviously. +Otherwise this is interpreted as a string to prepend to the Slave mailbox +name to make up a complete path. .br This option can be used outside any section for a global effect. In this case the appended string is made up according to the pattern diff --git a/src/socket.c b/src/socket.c @@ -63,6 +63,34 @@ socket_fail( conn_t *conn ) } #ifdef HAVE_LIBSSL +static void ATTR_PRINTFLIKE(1, 2) +print_ssl_errors( const char *fmt, ... ) +{ + char *action; + va_list va; + ulong err; + + va_start( va, fmt ); + nfvasprintf( &action, fmt, va ); + va_end( va ); + while ((err = ERR_get_error())) + error( "Error while %s: %s\n", action, ERR_error_string( err, 0 ) ); + free( action ); +} + +static int +print_ssl_socket_errors( const char *func, conn_t *conn ) +{ + ulong err; + int num = 0; + + while ((err = ERR_get_error())) { + error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) ); + num++; + } + return num; +} + static int ssl_return( const char *func, conn_t *conn, int ret ) { @@ -76,20 +104,20 @@ ssl_return( const char *func, conn_t *conn, int ret ) FALLTHROUGH case SSL_ERROR_WANT_READ: return 0; - case SSL_ERROR_SYSCALL: case SSL_ERROR_SSL: - if (!(err = ERR_get_error())) { - if (ret == 0) { + print_ssl_socket_errors( func, conn ); + break; + case SSL_ERROR_SYSCALL: + if (print_ssl_socket_errors( func, conn )) + break; + if (ret == 0) { case SSL_ERROR_ZERO_RETURN: - /* Callers take the short path out, so signal higher layers from here. */ - conn->state = SCK_EOF; - conn->read_callback( conn->callback_aux ); - return -1; - } - sys_error( "Socket error: secure %s %s", func, conn->name ); - } else { - error( "Socket error: secure %s %s: %s\n", func, conn->name, ERR_error_string( err, 0 ) ); + /* Callers take the short path out, so signal higher layers from here. */ + conn->state = SCK_EOF; + conn->read_callback( conn->callback_aux ); + return -1; } + sys_error( "Socket error: secure %s %s", func, conn->name ); break; default: error( "Socket error: secure %s %s: unhandled SSL error %d\n", func, conn->name, err ); @@ -176,22 +204,29 @@ verify_cert_host( const server_conf_t *conf, conn_t *sock ) trusted = (STACK_OF(X509_OBJECT) *)sock->conf->trusted_certs; for (i = 0; i < sk_X509_OBJECT_num( trusted ); i++) { - if (!X509_cmp( cert, X509_OBJECT_get0_X509( sk_X509_OBJECT_value( trusted, i ) ) )) + if (!X509_cmp( cert, X509_OBJECT_get0_X509( sk_X509_OBJECT_value( trusted, i ) ) )) { + X509_free( cert ); return 0; + } } err = SSL_get_verify_result( sock->ssl ); if (err != X509_V_OK) { error( "SSL error connecting %s: %s\n", sock->name, X509_verify_cert_error_string( err ) ); + X509_free( cert ); return -1; } if (!conf->host) { error( "SSL error connecting %s: Neither host nor matching certificate specified\n", sock->name ); + X509_free( cert ); return -1; } - return verify_hostname( cert, conf->host ); + int ret = verify_hostname( cert, conf->host ); + + X509_free( cert ); + return ret; } static int @@ -203,7 +238,15 @@ init_ssl_ctx( const server_conf_t *conf ) if (conf->SSLContext) return conf->ssl_ctx_valid; - mconf->SSLContext = SSL_CTX_new( SSLv23_client_method() ); +#if OPENSSL_VERSION_NUMBER >= 0x10100000L + const SSL_METHOD *method = TLS_client_method(); +#else + const SSL_METHOD *method = SSLv23_client_method(); +#endif + if (!(mconf->SSLContext = SSL_CTX_new( method ))) { + print_ssl_errors( "initializing SSL context" ); + return 0; + } if (!(conf->ssl_versions & SSLv3)) options |= SSL_OP_NO_SSLv3; @@ -221,25 +264,24 @@ init_ssl_ctx( const server_conf_t *conf ) SSL_CTX_set_options( mconf->SSLContext, options ); if (conf->cert_file && !SSL_CTX_load_verify_locations( mconf->SSLContext, conf->cert_file, 0 )) { - error( "Error while loading certificate file '%s': %s\n", - conf->cert_file, ERR_error_string( ERR_get_error(), 0 ) ); + print_ssl_errors( "loading certificate file '%s'", conf->cert_file ); return 0; } mconf->trusted_certs = (_STACK *)sk_X509_OBJECT_dup( X509_STORE_get0_objects( SSL_CTX_get_cert_store( mconf->SSLContext ) ) ); - if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext )) - warn( "Warning: Unable to load default certificate files: %s\n", - ERR_error_string( ERR_get_error(), 0 ) ); + if (mconf->system_certs && !SSL_CTX_set_default_verify_paths( mconf->SSLContext )) { + ulong err; + while ((err = ERR_get_error())) + warn( "Warning: Unable to load default certificate files: %s\n", ERR_error_string( err, 0 ) ); + } SSL_CTX_set_verify( mconf->SSLContext, SSL_VERIFY_NONE, NULL ); if (conf->client_certfile && !SSL_CTX_use_certificate_chain_file( mconf->SSLContext, conf->client_certfile)) { - error( "Error while loading client certificate file '%s': %s\n", - conf->client_certfile, ERR_error_string( ERR_get_error(), 0 ) ); + print_ssl_errors( "loading client certificate file '%s'", conf->client_certfile ); return 0; } if (conf->client_keyfile && !SSL_CTX_use_PrivateKey_file( mconf->SSLContext, conf->client_keyfile, SSL_FILETYPE_PEM)) { - error( "Error while loading client private key '%s': %s\n", - conf->client_keyfile, ERR_error_string( ERR_get_error(), 0 ) ); + print_ssl_errors( "loading client private key '%s'", conf->client_keyfile ); return 0; } @@ -270,10 +312,21 @@ socket_start_tls( conn_t *conn, void (*cb)( int ok, void *aux ) ) } init_wakeup( &conn->ssl_fake, ssl_fake_cb, conn ); - conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext ); - if (ssl_return( "set server name", conn, SSL_set_tlsext_host_name( conn->ssl, conn->conf->host ) ) < 0) + if (!(conn->ssl = SSL_new( ((server_conf_t *)conn->conf)->SSLContext ))) { + print_ssl_errors( "initializing SSL connection" ); + start_tls_p3( conn, 0 ); + return; + } + if (!SSL_set_tlsext_host_name( conn->ssl, conn->conf->host )) { + print_ssl_errors( "setting SSL server host name" ); + start_tls_p3( conn, 0 ); return; - SSL_set_fd( conn->ssl, conn->fd ); + } + if (!SSL_set_fd( conn->ssl, conn->fd )) { + print_ssl_errors( "setting SSL socket fd" ); + start_tls_p3( conn, 0 ); + return; + } SSL_set_mode( conn->ssl, SSL_MODE_ACCEPT_MOVING_WRITE_BUFFER ); socket_expect_read( conn, 1 ); conn->state = SCK_STARTTLS; @@ -545,8 +598,10 @@ static void socket_connected( conn_t *conn ) { #ifdef HAVE_IPV6 - freeaddrinfo( conn->addrs ); - conn->addrs = 0; + if (conn->addrs) { + freeaddrinfo( conn->addrs ); + conn->addrs = 0; + } #endif conf_notifier( &conn->notify, 0, POLLIN ); socket_expect_read( conn, 0 );