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: Robert Shearman <robertshearman@gmail.com> Signed-off-by: Junio C Hamano <gitster@pobox.com>
This commit is contained in:
parent
a0406b94d5
commit
684ec6c63c
@ -37,10 +37,11 @@ configuration file (shown with examples):
|
|||||||
Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
|
Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
|
||||||
|
|
||||||
[imap]
|
[imap]
|
||||||
Host = imap.server.com
|
Host = imap://imap.example.com
|
||||||
User = bob
|
User = bob
|
||||||
Pass = pwd
|
Pass = pwd
|
||||||
Port = 143
|
Port = 143
|
||||||
|
sslverify = false
|
||||||
..........................
|
..........................
|
||||||
|
|
||||||
|
|
||||||
|
4
Makefile
4
Makefile
@ -1208,7 +1208,9 @@ endif
|
|||||||
git-%$X: %.o $(GITLIBS)
|
git-%$X: %.o $(GITLIBS)
|
||||||
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
|
$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(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 $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
|
||||||
|
$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
|
||||||
|
|
||||||
http.o http-walker.o http-push.o transport.o: http.h
|
http.o http-walker.o http-push.o transport.o: http.h
|
||||||
|
|
||||||
|
@ -99,6 +99,11 @@
|
|||||||
#include <iconv.h>
|
#include <iconv.h>
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef NO_OPENSSL
|
||||||
|
#include <openssl/ssl.h>
|
||||||
|
#include <openssl/err.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
/* On most systems <limits.h> would have given us this, but
|
/* On most systems <limits.h> would have given us this, but
|
||||||
* not on some systems (e.g. GNU/Hurd).
|
* not on some systems (e.g. GNU/Hurd).
|
||||||
*/
|
*/
|
||||||
|
160
imap-send.c
160
imap-send.c
@ -23,6 +23,9 @@
|
|||||||
*/
|
*/
|
||||||
|
|
||||||
#include "cache.h"
|
#include "cache.h"
|
||||||
|
#ifdef NO_OPENSSL
|
||||||
|
typedef void *SSL;
|
||||||
|
#endif
|
||||||
|
|
||||||
typedef struct store_conf {
|
typedef struct store_conf {
|
||||||
char *name;
|
char *name;
|
||||||
@ -129,6 +132,8 @@ typedef struct imap_server_conf {
|
|||||||
int port;
|
int port;
|
||||||
char *user;
|
char *user;
|
||||||
char *pass;
|
char *pass;
|
||||||
|
int use_ssl;
|
||||||
|
int ssl_verify;
|
||||||
} imap_server_conf_t;
|
} imap_server_conf_t;
|
||||||
|
|
||||||
typedef struct imap_store_conf {
|
typedef struct imap_store_conf {
|
||||||
@ -148,6 +153,7 @@ typedef struct _list {
|
|||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
int fd;
|
int fd;
|
||||||
|
SSL *ssl;
|
||||||
} Socket_t;
|
} Socket_t;
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
@ -201,6 +207,7 @@ enum CAPABILITY {
|
|||||||
UIDPLUS,
|
UIDPLUS,
|
||||||
LITERALPLUS,
|
LITERALPLUS,
|
||||||
NAMESPACE,
|
NAMESPACE,
|
||||||
|
STARTTLS,
|
||||||
};
|
};
|
||||||
|
|
||||||
static const char *cap_list[] = {
|
static const char *cap_list[] = {
|
||||||
@ -208,6 +215,7 @@ static const char *cap_list[] = {
|
|||||||
"UIDPLUS",
|
"UIDPLUS",
|
||||||
"LITERAL+",
|
"LITERAL+",
|
||||||
"NAMESPACE",
|
"NAMESPACE",
|
||||||
|
"STARTTLS",
|
||||||
};
|
};
|
||||||
|
|
||||||
#define RESP_OK 0
|
#define RESP_OK 0
|
||||||
@ -225,19 +233,101 @@ static const char *Flags[] = {
|
|||||||
"Deleted",
|
"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
|
static void
|
||||||
socket_perror( const char *func, Socket_t *sock, int ret )
|
socket_perror( const char *func, Socket_t *sock, int ret )
|
||||||
{
|
{
|
||||||
if (ret < 0)
|
#ifndef NO_OPENSSL
|
||||||
perror( func );
|
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
|
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
|
static int
|
||||||
socket_read( Socket_t *sock, char *buf, int len )
|
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) {
|
if (n <= 0) {
|
||||||
socket_perror( "read", sock, n );
|
socket_perror( "read", sock, n );
|
||||||
close( sock->fd );
|
close( sock->fd );
|
||||||
@ -249,7 +339,13 @@ socket_read( Socket_t *sock, char *buf, int len )
|
|||||||
static int
|
static int
|
||||||
socket_write( Socket_t *sock, const char *buf, int len )
|
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) {
|
if (n != len) {
|
||||||
socket_perror( "write", sock, n );
|
socket_perror( "write", sock, n );
|
||||||
close( sock->fd );
|
close( sock->fd );
|
||||||
@ -258,6 +354,17 @@ socket_write( Socket_t *sock, const char *buf, int len )
|
|||||||
return n;
|
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 */
|
/* simple line buffering */
|
||||||
static int
|
static int
|
||||||
buffer_gets( buffer_t * b, char **s )
|
buffer_gets( buffer_t * b, char **s )
|
||||||
@ -875,7 +982,7 @@ imap_close_server( imap_store_t *ictx )
|
|||||||
|
|
||||||
if (imap->buf.sock.fd != -1) {
|
if (imap->buf.sock.fd != -1) {
|
||||||
imap_exec( ictx, NULL, "LOGOUT" );
|
imap_exec( ictx, NULL, "LOGOUT" );
|
||||||
close( imap->buf.sock.fd );
|
socket_shutdown( &imap->buf.sock );
|
||||||
}
|
}
|
||||||
free_list( imap->ns_personal );
|
free_list( imap->ns_personal );
|
||||||
free_list( imap->ns_other );
|
free_list( imap->ns_other );
|
||||||
@ -958,10 +1065,15 @@ imap_open_store( imap_server_conf_t *srvc )
|
|||||||
perror( "connect" );
|
perror( "connect" );
|
||||||
goto bail;
|
goto bail;
|
||||||
}
|
}
|
||||||
imap_info( "ok\n" );
|
|
||||||
|
|
||||||
imap->buf.sock.fd = s;
|
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 */
|
/* read the greeting string */
|
||||||
@ -986,7 +1098,18 @@ imap_open_store( imap_server_conf_t *srvc )
|
|||||||
goto bail;
|
goto bail;
|
||||||
|
|
||||||
if (!preauth) {
|
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");
|
imap_info ("Logging in...\n");
|
||||||
if (!srvc->user) {
|
if (!srvc->user) {
|
||||||
fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
|
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 %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
|
fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
|
||||||
goto bail;
|
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) {
|
if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
|
||||||
fprintf( stderr, "IMAP error: LOGIN failed\n" );
|
fprintf( stderr, "IMAP error: LOGIN failed\n" );
|
||||||
goto bail;
|
goto bail;
|
||||||
@ -1242,6 +1367,8 @@ static imap_server_conf_t server =
|
|||||||
0, /* port */
|
0, /* port */
|
||||||
NULL, /* user */
|
NULL, /* user */
|
||||||
NULL, /* pass */
|
NULL, /* pass */
|
||||||
|
0, /* use_ssl */
|
||||||
|
1, /* ssl_verify */
|
||||||
};
|
};
|
||||||
|
|
||||||
static char *imap_folder;
|
static char *imap_folder;
|
||||||
@ -1262,11 +1389,11 @@ git_imap_config(const char *key, const char *val, void *cb)
|
|||||||
if (!strcmp( "folder", key )) {
|
if (!strcmp( "folder", key )) {
|
||||||
imap_folder = xstrdup( val );
|
imap_folder = xstrdup( val );
|
||||||
} else if (!strcmp( "host", key )) {
|
} else if (!strcmp( "host", key )) {
|
||||||
{
|
if (!prefixcmp(val, "imap:"))
|
||||||
if (!prefixcmp(val, "imap:"))
|
val += 5;
|
||||||
val += 5;
|
else if (!prefixcmp(val, "imaps:")) {
|
||||||
if (!server.port)
|
val += 6;
|
||||||
server.port = 143;
|
server.use_ssl = 1;
|
||||||
}
|
}
|
||||||
if (!prefixcmp(val, "//"))
|
if (!prefixcmp(val, "//"))
|
||||||
val += 2;
|
val += 2;
|
||||||
@ -1280,6 +1407,8 @@ git_imap_config(const char *key, const char *val, void *cb)
|
|||||||
server.port = git_config_int( key, val );
|
server.port = git_config_int( key, val );
|
||||||
else if (!strcmp( "tunnel", key ))
|
else if (!strcmp( "tunnel", key ))
|
||||||
server.tunnel = xstrdup( val );
|
server.tunnel = xstrdup( val );
|
||||||
|
else if (!strcmp( "sslverify", key ))
|
||||||
|
server.ssl_verify = git_config_bool( key, val );
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1300,6 +1429,9 @@ main(int argc, char **argv)
|
|||||||
setup_git_directory_gently(&nongit_ok);
|
setup_git_directory_gently(&nongit_ok);
|
||||||
git_config(git_imap_config, NULL);
|
git_config(git_imap_config, NULL);
|
||||||
|
|
||||||
|
if (!server.port)
|
||||||
|
server.port = server.use_ssl ? 993 : 143;
|
||||||
|
|
||||||
if (!imap_folder) {
|
if (!imap_folder) {
|
||||||
fprintf( stderr, "no imap store specified\n" );
|
fprintf( stderr, "no imap store specified\n" );
|
||||||
return 1;
|
return 1;
|
||||||
|
Loading…
Reference in New Issue
Block a user