@ -23,6 +23,9 @@
@@ -23,6 +23,9 @@
*/
#include "cache.h"
#ifdef NO_OPENSSL
typedef void *SSL;
#endif
typedef struct store_conf {
char *name;
@ -129,6 +132,8 @@ typedef struct imap_server_conf {
@@ -129,6 +132,8 @@ typedef struct imap_server_conf {
int port;
char *user;
char *pass;
int use_ssl;
int ssl_verify;
} imap_server_conf_t;
typedef struct imap_store_conf {
@ -148,6 +153,7 @@ typedef struct _list {
@@ -148,6 +153,7 @@ typedef struct _list {
typedef struct {
int fd;
SSL *ssl;
} Socket_t;
typedef struct {
@ -201,6 +207,7 @@ enum CAPABILITY {
@@ -201,6 +207,7 @@ enum CAPABILITY {
UIDPLUS,
LITERALPLUS,
NAMESPACE,
STARTTLS,
};
static const char *cap_list[] = {
@ -208,6 +215,7 @@ static const char *cap_list[] = {
@@ -208,6 +215,7 @@ static const char *cap_list[] = {
"UIDPLUS",
"LITERAL+",
"NAMESPACE",
"STARTTLS",
};
#define RESP_OK 0
@ -225,19 +233,101 @@ static const char *Flags[] = {
@@ -225,19 +233,101 @@ static const char *Flags[] = {
"Deleted",
};
#ifndef NO_OPENSSL
static void ssl_socket_perror(const char *func)
{
fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
}
#endif
static void
socket_perror( const char *func, Socket_t *sock, int ret )
{
if (ret < 0)
perror( func );
#ifndef NO_OPENSSL
if (sock->ssl) {
int sslerr = SSL_get_error(sock->ssl, ret);
switch (sslerr) {
case SSL_ERROR_NONE:
break;
case SSL_ERROR_SYSCALL:
perror("SSL_connect");
break;
default:
ssl_socket_perror("SSL_connect");
break;
}
} else
#endif
{
if (ret < 0)
perror(func);
else
fprintf(stderr, "%s: unexpected EOF\n", func);
}
}
static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify)
{
#ifdef NO_OPENSSL
fprintf(stderr, "SSL requested but SSL support not compiled in\n");
return -1;
#else
SSL_METHOD *meth;
SSL_CTX *ctx;
int ret;
SSL_library_init();
SSL_load_error_strings();
if (use_tls_only)
meth = TLSv1_method();
else
fprintf( stderr, "%s: unexpected EOF\n", func );
meth = SSLv23_method();
if (!meth) {
ssl_socket_perror("SSLv23_method");
return -1;
}
ctx = SSL_CTX_new(meth);
if (verify)
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
if (!SSL_CTX_set_default_verify_paths(ctx)) {
ssl_socket_perror("SSL_CTX_set_default_verify_paths");
return -1;
}
sock->ssl = SSL_new(ctx);
if (!sock->ssl) {
ssl_socket_perror("SSL_new");
return -1;
}
if (!SSL_set_fd(sock->ssl, sock->fd)) {
ssl_socket_perror("SSL_set_fd");
return -1;
}
ret = SSL_connect(sock->ssl);
if (ret <= 0) {
socket_perror("SSL_connect", sock, ret);
return -1;
}
return 0;
#endif
}
static int
socket_read( Socket_t *sock, char *buf, int len )
{
ssize_t n = xread( sock->fd, buf, len );
ssize_t n;
#ifndef NO_OPENSSL
if (sock->ssl)
n = SSL_read(sock->ssl, buf, len);
else
#endif
n = xread( sock->fd, buf, len );
if (n <= 0) {
socket_perror( "read", sock, n );
close( sock->fd );
@ -249,7 +339,13 @@ socket_read( Socket_t *sock, char *buf, int len )
@@ -249,7 +339,13 @@ socket_read( Socket_t *sock, char *buf, int len )
static int
socket_write( Socket_t *sock, const char *buf, int len )
{
int n = write_in_full( sock->fd, buf, len );
int n;
#ifndef NO_OPENSSL
if (sock->ssl)
n = SSL_write(sock->ssl, buf, len);
else
#endif
n = write_in_full( sock->fd, buf, len );
if (n != len) {
socket_perror( "write", sock, n );
close( sock->fd );
@ -258,6 +354,17 @@ socket_write( Socket_t *sock, const char *buf, int len )
@@ -258,6 +354,17 @@ socket_write( Socket_t *sock, const char *buf, int len )
return n;
}
static void socket_shutdown(Socket_t *sock)
{
#ifndef NO_OPENSSL
if (sock->ssl) {
SSL_shutdown(sock->ssl);
SSL_free(sock->ssl);
}
#endif
close(sock->fd);
}
/* simple line buffering */
static int
buffer_gets( buffer_t * b, char **s )
@ -875,7 +982,7 @@ imap_close_server( imap_store_t *ictx )
@@ -875,7 +982,7 @@ imap_close_server( imap_store_t *ictx )
if (imap->buf.sock.fd != -1) {
imap_exec( ictx, NULL, "LOGOUT" );
close( imap->buf.sock.fd );
socket_shutdown( &imap->buf.sock );
}
free_list( imap->ns_personal );
free_list( imap->ns_other );
@ -958,10 +1065,15 @@ imap_open_store( imap_server_conf_t *srvc )
@@ -958,10 +1065,15 @@ imap_open_store( imap_server_conf_t *srvc )
perror( "connect" );
goto bail;
}
imap_info( "ok\n" );
imap->buf.sock.fd = s;
if (srvc->use_ssl &&
ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
close(s);
goto bail;
}
imap_info( "ok\n" );
}
/* read the greeting string */
@ -986,7 +1098,18 @@ imap_open_store( imap_server_conf_t *srvc )
@@ -986,7 +1098,18 @@ imap_open_store( imap_server_conf_t *srvc )
goto bail;
if (!preauth) {
#ifndef NO_OPENSSL
if (!srvc->use_ssl && CAP(STARTTLS)) {
if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
goto bail;
if (ssl_socket_connect(&imap->buf.sock, 1,
srvc->ssl_verify))
goto bail;
/* capabilities may have changed, so get the new capabilities */
if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
goto bail;
}
#endif
imap_info ("Logging in...\n");
if (!srvc->user) {
fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
@ -1014,7 +1137,9 @@ imap_open_store( imap_server_conf_t *srvc )
@@ -1014,7 +1137,9 @@ imap_open_store( imap_server_conf_t *srvc )
fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
goto bail;
}
imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
if (!imap->buf.sock.ssl)
imap_warn( "*** IMAP Warning *** Password is being "
"sent in the clear\n" );
if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
fprintf( stderr, "IMAP error: LOGIN failed\n" );
goto bail;
@ -1242,6 +1367,8 @@ static imap_server_conf_t server =
@@ -1242,6 +1367,8 @@ static imap_server_conf_t server =
0, /* port */
NULL, /* user */
NULL, /* pass */
0, /* use_ssl */
1, /* ssl_verify */
};
static char *imap_folder;
@ -1262,11 +1389,11 @@ git_imap_config(const char *key, const char *val, void *cb)
@@ -1262,11 +1389,11 @@ git_imap_config(const char *key, const char *val, void *cb)
if (!strcmp( "folder", key )) {
imap_folder = xstrdup( val );
} else if (!strcmp( "host", key )) {
{
if (!prefixcmp(val, "imap:"))
val += 5;
if (!server.port)
server.port = 143;
if (!prefixcmp(val, "imap:"))
val += 5;
else if (!prefixcmp(val, "imaps:")) {
val += 6;
server.use_ssl = 1;
}
if (!prefixcmp(val, "//"))
val += 2;
@ -1280,6 +1407,8 @@ git_imap_config(const char *key, const char *val, void *cb)
@@ -1280,6 +1407,8 @@ git_imap_config(const char *key, const char *val, void *cb)
server.port = git_config_int( key, val );
else if (!strcmp( "tunnel", key ))
server.tunnel = xstrdup( val );
else if (!strcmp( "sslverify", key ))
server.ssl_verify = git_config_bool( key, val );
return 0;
}
@ -1300,6 +1429,9 @@ main(int argc, char **argv)
@@ -1300,6 +1429,9 @@ main(int argc, char **argv)
setup_git_directory_gently(&nongit_ok);
git_config(git_imap_config, NULL);
if (!server.port)
server.port = server.use_ssl ? 993 : 143;
if (!imap_folder) {
fprintf( stderr, "no imap store specified\n" );
return 1;