{"thread":{"id":"14382","subject":"[PATCH 2/4] git-imap-send: Add support for SSL.","startedAt":"2008-07-09T21:29:00Z","lastAt":"2008-07-10T05:33:17Z","messageCount":5,"participants":["Robert Shearman","Junio C Hamano","Linus Torvalds","Mike Hommey"],"isPatch":true,"patchVersion":1,"patchTotal":4},"messages":[{"id":"82755","messageId":"1215638942-25010-2-git-send-email-robertshearman@gmail.com","threadId":"14382","inReplyTo":null,"subject":"[PATCH 2/4] git-imap-send: Add support for SSL.","fromName":"Robert Shearman","fromEmail":"robertshearman@gmail.com","sentAt":"2008-07-09T21:29:00Z","receivedAt":"2008-07-09T21:29:00Z","isPatch":true,"sender":{"key":"robertshearman@gmail.com","avatar":null},"body":"From: Robert Shearman <rob@codeweavers.com>\n\nAllow SSL to be used when a imaps:// URL is used for the host name.\n\nAlso, automatically use TLS when not using imaps:// by using the IMAP STARTTLS command, if the server supports it.\n\nTested with Courier and Gimap IMAP servers.\n\nSigned-off-by: Robert Shearman <robertshearman@gmail.com>\n---\n Documentation/git-imap-send.txt |    3 +-\n Makefile                        |    4 +-\n imap-send.c                     |  166 +++++++++++++++++++++++++++++++++++----\n 3 files changed, 156 insertions(+), 17 deletions(-)\n\ndiff --git a/Documentation/git-imap-send.txt b/Documentation/git-imap-send.txt\nindex b3d8da3..136c82b 100644\n--- a/Documentation/git-imap-send.txt\n+++ b/Documentation/git-imap-send.txt\n@@ -37,10 +37,11 @@ configuration file (shown with examples):\n     Tunnel = \"ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null\"\n \n [imap]\n-    Host = imap.server.com\n+    Host = imap://imap.example.com\n     User = bob\n     Pass = pwd\n     Port = 143\n+    sslverify = false\n ..........................\n \n \ndiff --git a/Makefile b/Makefile\nindex 4796565..55ec6ee 100644\n--- a/Makefile\n+++ b/Makefile\n@@ -1193,7 +1193,9 @@ endif\n git-%$X: %.o $(GITLIBS)\n \t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)\n \n-git-imap-send$X: imap-send.o $(LIB_FILE)\n+git-imap-send$X: imap-send.o $(GITLIBS)\n+\t$(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \\\n+\t\t$(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)\n \n http.o http-walker.o http-push.o transport.o: http.h\n \ndiff --git a/imap-send.c b/imap-send.c\nindex 24d76a7..26d1dba 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -23,6 +23,12 @@\n  */\n \n #include \"cache.h\"\n+#ifdef NO_OPENSSL\n+typedef void *SSL;\n+#else\n+# include <openssl/ssl.h>\n+# include <openssl/err.h>\n+#endif\n \n typedef struct store_conf {\n \tchar *name;\n@@ -129,6 +135,8 @@ typedef struct imap_server_conf {\n \tint port;\n \tchar *user;\n \tchar *pass;\n+\tint use_ssl;\n+\tint ssl_verify;\n } imap_server_conf_t;\n \n typedef struct imap_store_conf {\n@@ -148,6 +156,7 @@ typedef struct _list {\n \n typedef struct {\n \tint fd;\n+\tSSL *ssl;\n } Socket_t;\n \n typedef struct {\n@@ -201,6 +210,7 @@ enum CAPABILITY {\n \tUIDPLUS,\n \tLITERALPLUS,\n \tNAMESPACE,\n+\tSTARTTLS,\n };\n \n static const char *cap_list[] = {\n@@ -208,6 +218,7 @@ static const char *cap_list[] = {\n \t\"UIDPLUS\",\n \t\"LITERAL+\",\n \t\"NAMESPACE\",\n+\t\"STARTTLS\",\n };\n \n #define RESP_OK    0\n@@ -225,19 +236,101 @@ static const char *Flags[] = {\n \t\"Deleted\",\n };\n \n+#ifndef NO_OPENSSL\n+static void ssl_socket_perror(const char *func)\n+{\n+\tfprintf(stderr, \"%s: %s\\n\", func, ERR_error_string(ERR_get_error(), 0));\n+}\n+#endif\n+\n static void\n socket_perror( const char *func, Socket_t *sock, int ret )\n {\n-\tif (ret < 0)\n-\t\tperror( func );\n+#ifndef NO_OPENSSL\n+\tif (sock->ssl) {\n+\t\tint sslerr = SSL_get_error(sock->ssl, ret);\n+\t\tswitch (sslerr) {\n+\t\tcase SSL_ERROR_NONE:\n+\t\t\tbreak;\n+\t\tcase SSL_ERROR_SYSCALL:\n+\t\t\tperror(\"SSL_connect\");\n+\t\t\tbreak;\n+\t\tdefault:\n+\t\t\tssl_socket_perror(\"SSL_connect\");\n+\t\t\tbreak;\n+\t\t}\n+\t} else\n+#endif\n+\t{\n+\t\tif (ret < 0)\n+\t\t\tperror(func);\n+\t\telse\n+\t\t\tfprintf(stderr, \"%s: unexpected EOF\\n\", func);\n+\t}\n+}\n+\n+static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify)\n+{\n+#ifdef NO_OPENSSL\n+\tfprintf(stderr, \"SSL requested but SSL support not compiled in\\n\");\n+\treturn -1;\n+#else\n+\tSSL_METHOD *meth;\n+\tSSL_CTX *ctx;\n+\tint ret;\n+\n+\tSSL_library_init();\n+\tSSL_load_error_strings();\n+\n+\tif (use_tls_only)\n+\t\tmeth = TLSv1_method();\n \telse\n-\t\tfprintf( stderr, \"%s: unexpected EOF\\n\", func );\n+\t\tmeth = SSLv23_method();\n+\n+\tif (!meth) {\n+\t\tssl_socket_perror(\"SSLv23_method\");\n+\t\treturn -1;\n+\t}\n+\n+\tctx = SSL_CTX_new(meth);\n+\n+\tif (verify)\n+\t\tSSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);\n+\n+\tif (!SSL_CTX_set_default_verify_paths(ctx)) {\n+\t\tssl_socket_perror(\"SSL_CTX_set_default_verify_paths\");\n+\t\treturn -1;\n+\t}\n+\tsock->ssl = SSL_new(ctx);\n+\tif (!sock->ssl) {\n+\t\tssl_socket_perror(\"SSL_new\");\n+\t\treturn -1;\n+\t}\n+\tif (!SSL_set_fd(sock->ssl, sock->fd)) {\n+\t\tssl_socket_perror(\"SSL_set_fd\");\n+\t\treturn -1;\n+\t}\n+\n+\tret = SSL_connect(sock->ssl);\n+\tif (ret <= 0) {\n+\t\tsocket_perror(\"SSL_connect\", sock, ret);\n+\t\treturn -1;\n+\t}\n+\n+\treturn 0;\n+#endif\n }\n \n static int\n socket_read( Socket_t *sock, char *buf, int len )\n {\n-\tssize_t n = xread( sock->fd, buf, len );\n+\tssize_t n;\n+#ifndef NO_OPENSSL\n+\tif (sock->ssl)\n+\t\tn = SSL_read(sock->ssl, buf, len);\n+\telse\n+#endif\n+\t\tn = xread( sock->fd, buf, len );\n \tif (n <= 0) {\n \t\tsocket_perror( \"read\", sock, n );\n \t\tclose( sock->fd );\n@@ -249,7 +342,13 @@ socket_read( Socket_t *sock, char *buf, int len )\n static int\n socket_write( Socket_t *sock, const char *buf, int len )\n {\n-\tint n = write_in_full( sock->fd, buf, len );\n+\tint n;\n+#ifndef NO_OPENSSL\n+\tif (sock->ssl)\n+\t\tn = SSL_write(sock->ssl, buf, len);\n+\telse\n+#endif\n+\t\tn = write_in_full( sock->fd, buf, len );\n \tif (n != len) {\n \t\tsocket_perror( \"write\", sock, n );\n \t\tclose( sock->fd );\n@@ -258,6 +357,17 @@ socket_write( Socket_t *sock, const char *buf, int len )\n \treturn n;\n }\n \n+static void socket_shutdown(Socket_t *sock)\n+{\n+#ifndef NO_OPENSSL\n+\tif (sock->ssl) {\n+\t\tSSL_shutdown(sock->ssl);\n+\t\tSSL_free(sock->ssl);\n+\t}\n+#endif\n+\tclose(sock->fd);\n+}\n+\n /* simple line buffering */\n static int\n buffer_gets( buffer_t * b, char **s )\n@@ -875,7 +985,7 @@ imap_close_server( imap_store_t *ictx )\n \n \tif (imap->buf.sock.fd != -1) {\n \t\timap_exec( ictx, NULL, \"LOGOUT\" );\n-\t\tclose( imap->buf.sock.fd );\n+\t\tsocket_shutdown( &imap->buf.sock );\n \t}\n \tfree_list( imap->ns_personal );\n \tfree_list( imap->ns_other );\n@@ -906,6 +1016,7 @@ imap_open_store( imap_server_conf_t *srvc )\n \n \tctx->imap = imap = xcalloc( sizeof(*imap), 1 );\n \timap->buf.sock.fd = -1;\n+\timap->buf.sock.ssl = NULL;\n \timap->in_progress_append = &imap->in_progress;\n \n \t/* open connection to IMAP server */\n@@ -958,10 +1069,15 @@ imap_open_store( imap_server_conf_t *srvc )\n \t\t\tperror( \"connect\" );\n \t\t\tgoto bail;\n \t\t}\n-\t\timap_info( \"ok\\n\" );\n-\n+\t\t\n \t\timap->buf.sock.fd = s;\n \n+\t\tif (srvc->use_ssl &&\n+\t\t    ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {\n+\t\t\tclose(s);\n+\t\t\tgoto bail;\n+\t\t}\n+\t\timap_info( \"ok\\n\" );\n \t}\n \n \t/* read the greeting string */\n@@ -986,7 +1102,18 @@ imap_open_store( imap_server_conf_t *srvc )\n \t\tgoto bail;\n \n \tif (!preauth) {\n-\n+#ifndef NO_OPENSSL\n+\t\tif (!srvc->use_ssl && CAP(STARTTLS)) {\n+\t\t\tif (imap_exec(ctx, 0, \"STARTTLS\") != RESP_OK)\n+\t\t\t\tgoto bail;\n+\t\t\tif (ssl_socket_connect(&imap->buf.sock, 1,\n+\t\t\t\t\t       srvc->ssl_verify))\n+\t\t\t\tgoto bail;\n+\t\t\t/* capabilities may have changed, so get the new capabilities */\n+\t\t\tif (imap_exec(ctx, 0, \"CAPABILITY\") != RESP_OK)\n+\t\t\t\tgoto bail;\n+\t\t}\n+#endif\n \t\timap_info (\"Logging in...\\n\");\n \t\tif (!srvc->user) {\n \t\t\tfprintf( stderr, \"Skipping server %s, no user\\n\", srvc->host );\n@@ -1014,7 +1141,9 @@ imap_open_store( imap_server_conf_t *srvc )\n \t\t\tfprintf( stderr, \"Skipping account %s@%s, server forbids LOGIN\\n\", srvc->user, srvc->host );\n \t\t\tgoto bail;\n \t\t}\n-\t\timap_warn( \"*** IMAP Warning *** Password is being sent in the clear\\n\" );\n+\t\tif (!imap->buf.sock.ssl)\n+\t\t\timap_warn( \"*** IMAP Warning *** Password is being \"\n+\t\t\t\t   \"sent in the clear\\n\" );\n \t\tif (imap_exec( ctx, NULL, \"LOGIN \\\"%s\\\" \\\"%s\\\"\", srvc->user, srvc->pass ) != RESP_OK) {\n \t\t\tfprintf( stderr, \"IMAP error: LOGIN failed\\n\" );\n \t\t\tgoto bail;\n@@ -1242,6 +1371,8 @@ static imap_server_conf_t server =\n \t0,\t/* port */\n \tNULL,\t/* user */\n \tNULL,\t/* pass */\n+\t0,   \t/* use_ssl */\n+\t1,   \t/* ssl_verify */\n };\n \n static char *imap_folder;\n@@ -1262,11 +1393,11 @@ git_imap_config(const char *key, const char *val, void *cb)\n \tif (!strcmp( \"folder\", key )) {\n \t\timap_folder = xstrdup( val );\n \t} else if (!strcmp( \"host\", key )) {\n-\t\t{\n-\t\t\tif (!prefixcmp(val, \"imap:\"))\n-\t\t\t\tval += 5;\n-\t\t\tif (!server.port)\n-\t\t\t\tserver.port = 143;\n+\t\tif (!prefixcmp(val, \"imap:\"))\n+\t\t\tval += 5;\n+\t\telse if (!prefixcmp(val, \"imaps:\")) {\n+\t\t\tval += 6;\n+\t\t\tserver.use_ssl = 1;\n \t\t}\n \t\tif (!prefixcmp(val, \"//\"))\n \t\t\tval += 2;\n@@ -1280,6 +1411,8 @@ git_imap_config(const char *key, const char *val, void *cb)\n \t\tserver.port = git_config_int( key, val );\n \telse if (!strcmp( \"tunnel\", key ))\n \t\tserver.tunnel = xstrdup( val );\n+\telse if (!strcmp( \"ssl_verify\", key ))\n+\t\tserver.ssl_verify = git_config_bool( key, val );\n \treturn 0;\n }\n \n@@ -1300,6 +1433,9 @@ main(int argc, char **argv)\n \tsetup_git_directory_gently(&nongit_ok);\n \tgit_config(git_imap_config, NULL);\n \n+\tif (!server.port)\n+\t\tserver.port = server.use_ssl ? 993 : 143;\n+\n \tif (!imap_folder) {\n \t\tfprintf( stderr, \"no imap store specified\\n\" );\n \t\treturn 1;\n-- \n1.5.6.2.224.g26efb.dirty\n"},{"id":"82765","messageId":"7v8wwa5ycf.fsf@gitster.siamese.dyndns.org","threadId":"14382","inReplyTo":"1215638942-25010-2-git-send-email-robertshearman@gmail.com","subject":"Re: [PATCH 2/4] git-imap-send: Add support for SSL.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-10T01:20:32Z","receivedAt":"2008-07-10T01:20:32Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Robert Shearman <robertshearman@gmail.com> writes:\n\n> diff --git a/imap-send.c b/imap-send.c\n> index 24d76a7..26d1dba 100644\n> --- a/imap-send.c\n> +++ b/imap-send.c\n> @@ -23,6 +23,12 @@\n>   */\n>  \n>  #include \"cache.h\"\n> +#ifdef NO_OPENSSL\n> +typedef void *SSL;\n> +#else\n> +# include <openssl/ssl.h>\n> +# include <openssl/err.h>\n> +#endif\n\nThis unfortunately is causing compilation issues.  <openssl/ssl.h> wants\nto include <ctype.h> and gets upset by seeing our isalpha() and friends\nthat are defined indirectly in \"cache.h\" expanded.\n\nIn <ctype.h> (on FC9), isCHARACTERISTC() are defined like this:\n\n\t#define __exctype(name) extern int name (int) __THROW\n\t...\n        __exctype (isalnum);\n        __exctype (isalpha);\n\nbut we have been using our own locale agonistic and signed-chars safe\nmacros defined in git-compat-util.h.  Including <ctype.h> breaks at the\nsyntax level, but more importantly if the system <ctype.h> redefines\nisalpha() and friends as macro, then it would break _our_ code that expect\nthese macros are the sane_ctype[] based ones we have.\n\nA hack like the one attached below would make it \"work\" but it is too\nugly.  Probably we need to bite the bullet and rename ours not to\ncollide, so that external library headers can safely include <ctype.h>.\n\nSigh...\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 8c7e114..0af6406 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -302,6 +302,7 @@ static inline int has_extension(const char *filename, const char *ext)\n }\n \n /* Sane ctype - no locale, and works with signed chars */\n+#define _CTYPE_H\n #undef isspace\n #undef isdigit\n #undef isalpha\n"},{"id":"82766","messageId":"alpine.LFD.1.10.0807091825500.11076@woody.linux-foundation.org","threadId":"14382","inReplyTo":"7v8wwa5ycf.fsf@gitster.siamese.dyndns.org","subject":"Re: [PATCH 2/4] git-imap-send: Add support for SSL.","fromName":"Linus Torvalds","fromEmail":"torvalds@linux-foundation.org","sentAt":"2008-07-10T01:31:02Z","receivedAt":"2008-07-10T01:31:02Z","isPatch":true,"sender":{"key":"torvalds@linux-foundation.org","avatar":"https://avatars.githubusercontent.com/u/1024025?v=4"},"body":"\n\nOn Wed, 9 Jul 2008, Junio C Hamano wrote:\n> \n> A hack like the one attached below would make it \"work\" but it is too\n> ugly.  Probably we need to bite the bullet and rename ours not to\n> collide, so that external library headers can safely include <ctype.h>.\n\nWe should be able to safely include <ctype.h> as-is.\n\nIt should just happen _before_ including git-compat-util.h. That's why \ngit-compat-util.h does all those #undef's - exactly because ctype.h does \nget included on various systems by various header files when \ngit-compat-util.h includes all those other files.\n\nSo the problem with Robert's patch is that it happens after \"cache.h\". The \nsystem headers should be included at the top of git-compat-util.h, or \nbefore it.\n\n\t\tLinus\n"},{"id":"82768","messageId":"7v1w225wdy.fsf@gitster.siamese.dyndns.org","threadId":"14382","inReplyTo":"alpine.LFD.1.10.0807091825500.11076@woody.linux-foundation.org","subject":"Re: [PATCH 2/4] git-imap-send: Add support for SSL.","fromName":"Junio C Hamano","fromEmail":"gitster@pobox.com","sentAt":"2008-07-10T02:02:49Z","receivedAt":"2008-07-10T02:02:49Z","isPatch":true,"sender":{"key":"gitster@pobox.com","avatar":"https://avatars.githubusercontent.com/u/54884?v=4"},"body":"Linus Torvalds <torvalds@linux-foundation.org> writes:\n\n> We should be able to safely include <ctype.h> as-is.\n>\n> It should just happen _before_ including git-compat-util.h. That's why \n> git-compat-util.h does all those #undef's - exactly because ctype.h does \n> get included on various systems by various header files when \n> git-compat-util.h includes all those other files.\n\nEh, that's true,... although I am not convinced we should keep doing this.\n\nWith and without NO_OPENSSL, on top of Robert's patch, these seem to pass\ncompile test.\n\n--\n\n git-compat-util.h |    5 +++++\n imap-send.c       |    4 ----\n 2 files changed, 5 insertions(+), 4 deletions(-)\n\ndiff --git a/git-compat-util.h b/git-compat-util.h\nindex 545df59..65c4671 100644\n--- a/git-compat-util.h\n+++ b/git-compat-util.h\n@@ -99,6 +99,11 @@\n #include <iconv.h>\n #endif\n \n+#ifndef NO_OPENSSL\n+#include <openssl/ssl.h>\n+#include <openssl/err.h>\n+#endif\n+\n /* On most systems <limits.h> would have given us this, but\n  * not on some systems (e.g. GNU/Hurd).\n  */\ndiff --git a/imap-send.c b/imap-send.c\nindex 9dc5d08..8026334 100644\n--- a/imap-send.c\n+++ b/imap-send.c\n@@ -25,9 +25,6 @@\n #include \"cache.h\"\n #ifdef NO_OPENSSL\n typedef void *SSL;\n-#else\n-# include <openssl/ssl.h>\n-# include <openssl/err.h>\n #endif\n \n typedef struct store_conf {\n"},{"id":"82777","messageId":"20080710053317.GC24520@glandium.org","threadId":"14382","inReplyTo":"1215638942-25010-2-git-send-email-robertshearman@gmail.com","subject":"Re: [PATCH 2/4] git-imap-send: Add support for SSL.","fromName":"Mike Hommey","fromEmail":"mh@glandium.org","sentAt":"2008-07-10T05:33:17Z","receivedAt":"2008-07-10T05:33:17Z","isPatch":true,"sender":{"key":"mh@glandium.org","avatar":"https://avatars.githubusercontent.com/u/1038527?v=4"},"body":"On Wed, Jul 09, 2008 at 10:29:00PM +0100, Robert Shearman wrote:\n> From: Robert Shearman <rob@codeweavers.com>\n> \n> Allow SSL to be used when a imaps:// URL is used for the host name.\n> \n> Also, automatically use TLS when not using imaps:// by using the IMAP STARTTLS command, if the server supports it.\n> \n> Tested with Courier and Gimap IMAP servers.\n\nPlease do an alternative implementation using GnuTLS. Why ? Because curl\ncan be built against openssl or gnutls, and it would be sad if git\nended up depending on both indirectly.\n\nMike\n"}]}