Commit 684ec6c6 authored by Robert Shearman's avatar Robert Shearman Committed by Junio C Hamano

git-imap-send: Support SSL

Allow SSL to be used when a imaps:// URL is used for the host name.

Also, automatically use TLS when not using imaps:// by using the IMAP
STARTTLS command, if the server supports it.

Tested with Courier and Gimap IMAP servers.
Signed-off-by: default avatarRobert Shearman <[email protected]>
Signed-off-by: default avatarJunio C Hamano <[email protected]>
parent a0406b94
......@@ -37,10 +37,11 @@ configuration file (shown with examples):
Tunnel = "ssh -q [email protected] /usr/bin/imapd ./Maildir 2> /dev/null"
[imap]
Host = imap.server.com
Host = imap://imap.example.com
User = bob
Pass = pwd
Port = 143
sslverify = false
..........................
......
......@@ -1208,7 +1208,9 @@ endif
git-%$X: %.o $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o [email protected] $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
git-imap-send$X: imap-send.o $(LIB_FILE)
git-imap-send$X: imap-send.o $(GITLIBS)
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o [email protected] $(ALL_LDFLAGS) $(filter %.o,$^) \
$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
http.o http-walker.o http-push.o transport.o: http.h
......
......@@ -99,6 +99,11 @@
#include <iconv.h>
#endif
#ifndef NO_OPENSSL
#include <openssl/ssl.h>
#include <openssl/err.h>
#endif
/* On most systems <limits.h> would have given us this, but
* not on some systems (e.g. GNU/Hurd).
*/
......
......@@ -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 {
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 {
typedef struct {
int fd;
SSL *ssl;
} Socket_t;
typedef struct {
......@@ -201,6 +207,7 @@ enum CAPABILITY {
UIDPLUS,
LITERALPLUS,
NAMESPACE,
STARTTLS,
};
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[] = {
"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 )
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 )
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 )
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 )
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 )
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 )
fprintf( stderr, "Skipping account %[email protected]%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 =
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)
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)
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)
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;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment