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"
|
||||
|
||||
[imap]
|
||||
Host = imap.server.com
|
||||
Host = imap://imap.example.com
|
||||
User = bob
|
||||
Pass = pwd
|
||||
Port = 143
|
||||
sslverify = false
|
||||
..........................
|
||||
|
||||
|
||||
|
4
Makefile
4
Makefile
@ -1208,7 +1208,9 @@ endif
|
||||
git-%$X: %.o $(GITLIBS)
|
||||
$(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
|
||||
|
||||
|
@ -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).
|
||||
*/
|
||||
|
160
imap-send.c
160
imap-send.c
@ -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 %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 =
|
||||
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;
|
||||
|
Loading…
Reference in New Issue
Block a user