@matheusbsbs / matheus-bagliano / commits / 684ec6c63c
Fork 0

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>

Robert Shearman committed Jul 9, 2008 at 22:29 UTC 684ec6c63cd92a3755500b3b63f3d5ad9f6828b2
4 files changed +156 -16
Documentation/git-imap-send.txt
+2 -1
@@ -37,10 +37,11 @@ configuration file (shown with examples):
37 Tunnel = "ssh -q user@server.com /usr/bin/imapd ./Maildir 2> /dev/null"
38
39 [imap]
40 - Host = imap.server.com
40 + Host = imap://imap.example.com
41 User = bob
42 Pass = pwd
43 Port = 143
44 + sslverify = false
45 ..........................
46
47
Makefile
+3 -1
@@ -1208,7 +1208,9 @@ endif
1208 git-%$X: %.o $(GITLIBS)
1209 $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) $(LIBS)
1210
1211 -git-imap-send$X: imap-send.o $(LIB_FILE)
1211 +git-imap-send$X: imap-send.o $(GITLIBS)
1212 + $(QUIET_LINK)$(CC) $(ALL_CFLAGS) -o $@ $(ALL_LDFLAGS) $(filter %.o,$^) \
1213 + $(LIBS) $(OPENSSL_LINK) $(OPENSSL_LIBSSL)
1214
1215 http.o http-walker.o http-push.o transport.o: http.h
1216
git-compat-util.h
+5
@@ -99,6 +99,11 @@
99 #include <iconv.h>
100 #endif
101
102 +#ifndef NO_OPENSSL
103 +#include <openssl/ssl.h>
104 +#include <openssl/err.h>
105 +#endif
106 +
107 /* On most systems <limits.h> would have given us this, but
108 * not on some systems (e.g. GNU/Hurd).
109 */
imap-send.c
+146 -14
@@ -23,6 +23,9 @@
23 */
24
25 #include "cache.h"
26 +#ifdef NO_OPENSSL
27 +typedef void *SSL;
28 +#endif
29
30 typedef struct store_conf {
31 char *name;
@@ -129,6 +132,8 @@ typedef struct imap_server_conf {
132 int port;
133 char *user;
134 char *pass;
135 + int use_ssl;
136 + int ssl_verify;
137 } imap_server_conf_t;
138
139 typedef struct imap_store_conf {
@@ -148,6 +153,7 @@ typedef struct _list {
153
154 typedef struct {
155 int fd;
156 + SSL *ssl;
157 } Socket_t;
158
159 typedef struct {
@@ -201,6 +207,7 @@ enum CAPABILITY {
207 UIDPLUS,
208 LITERALPLUS,
209 NAMESPACE,
210 + STARTTLS,
211 };
212
213 static const char *cap_list[] = {
@@ -208,6 +215,7 @@ static const char *cap_list[] = {
215 "UIDPLUS",
216 "LITERAL+",
217 "NAMESPACE",
218 + "STARTTLS",
219 };
220
221 #define RESP_OK 0
@@ -225,19 +233,101 @@ static const char *Flags[] = {
233 "Deleted",
234 };
235
236 +#ifndef NO_OPENSSL
237 +static void ssl_socket_perror(const char *func)
238 +{
239 + fprintf(stderr, "%s: %s\n", func, ERR_error_string(ERR_get_error(), 0));
240 +}
241 +#endif
242 +
243 static void
244 socket_perror( const char *func, Socket_t *sock, int ret )
245 {
231 - if (ret < 0)
232 - perror( func );
246 +#ifndef NO_OPENSSL
247 + if (sock->ssl) {
248 + int sslerr = SSL_get_error(sock->ssl, ret);
249 + switch (sslerr) {
250 + case SSL_ERROR_NONE:
251 + break;
252 + case SSL_ERROR_SYSCALL:
253 + perror("SSL_connect");
254 + break;
255 + default:
256 + ssl_socket_perror("SSL_connect");
257 + break;
258 + }
259 + } else
260 +#endif
261 + {
262 + if (ret < 0)
263 + perror(func);
264 + else
265 + fprintf(stderr, "%s: unexpected EOF\n", func);
266 + }
267 +}
268 +
269 +static int ssl_socket_connect(Socket_t *sock, int use_tls_only, int verify)
270 +{
271 +#ifdef NO_OPENSSL
272 + fprintf(stderr, "SSL requested but SSL support not compiled in\n");
273 + return -1;
274 +#else
275 + SSL_METHOD *meth;
276 + SSL_CTX *ctx;
277 + int ret;
278 +
279 + SSL_library_init();
280 + SSL_load_error_strings();
281 +
282 + if (use_tls_only)
283 + meth = TLSv1_method();
284 else
234 - fprintf( stderr, "%s: unexpected EOF\n", func );
285 + meth = SSLv23_method();
286 +
287 + if (!meth) {
288 + ssl_socket_perror("SSLv23_method");
289 + return -1;
290 + }
291 +
292 + ctx = SSL_CTX_new(meth);
293 +
294 + if (verify)
295 + SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER, NULL);
296 +
297 + if (!SSL_CTX_set_default_verify_paths(ctx)) {
298 + ssl_socket_perror("SSL_CTX_set_default_verify_paths");
299 + return -1;
300 + }
301 + sock->ssl = SSL_new(ctx);
302 + if (!sock->ssl) {
303 + ssl_socket_perror("SSL_new");
304 + return -1;
305 + }
306 + if (!SSL_set_fd(sock->ssl, sock->fd)) {
307 + ssl_socket_perror("SSL_set_fd");
308 + return -1;
309 + }
310 +
311 + ret = SSL_connect(sock->ssl);
312 + if (ret <= 0) {
313 + socket_perror("SSL_connect", sock, ret);
314 + return -1;
315 + }
316 +
317 + return 0;
318 +#endif
319 }
320
321 static int
322 socket_read( Socket_t *sock, char *buf, int len )
323 {
240 - ssize_t n = xread( sock->fd, buf, len );
324 + ssize_t n;
325 +#ifndef NO_OPENSSL
326 + if (sock->ssl)
327 + n = SSL_read(sock->ssl, buf, len);
328 + else
329 +#endif
330 + n = xread( sock->fd, buf, len );
331 if (n <= 0) {
332 socket_perror( "read", sock, n );
333 close( sock->fd );
@@ -249,7 +339,13 @@ socket_read( Socket_t *sock, char *buf, int len )
339 static int
340 socket_write( Socket_t *sock, const char *buf, int len )
341 {
252 - int n = write_in_full( sock->fd, buf, len );
342 + int n;
343 +#ifndef NO_OPENSSL
344 + if (sock->ssl)
345 + n = SSL_write(sock->ssl, buf, len);
346 + else
347 +#endif
348 + n = write_in_full( sock->fd, buf, len );
349 if (n != len) {
350 socket_perror( "write", sock, n );
351 close( sock->fd );
@@ -258,6 +354,17 @@ socket_write( Socket_t *sock, const char *buf, int len )
354 return n;
355 }
356
357 +static void socket_shutdown(Socket_t *sock)
358 +{
359 +#ifndef NO_OPENSSL
360 + if (sock->ssl) {
361 + SSL_shutdown(sock->ssl);
362 + SSL_free(sock->ssl);
363 + }
364 +#endif
365 + close(sock->fd);
366 +}
367 +
368 /* simple line buffering */
369 static int
370 buffer_gets( buffer_t * b, char **s )
@@ -875,7 +982,7 @@ imap_close_server( imap_store_t *ictx )
982
983 if (imap->buf.sock.fd != -1) {
984 imap_exec( ictx, NULL, "LOGOUT" );
878 - close( imap->buf.sock.fd );
985 + socket_shutdown( &imap->buf.sock );
986 }
987 free_list( imap->ns_personal );
988 free_list( imap->ns_other );
@@ -958,10 +1065,15 @@ imap_open_store( imap_server_conf_t *srvc )
1065 perror( "connect" );
1066 goto bail;
1067 }
961 - imap_info( "ok\n" );
1068
1069 imap->buf.sock.fd = s;
1070
1071 + if (srvc->use_ssl &&
1072 + ssl_socket_connect(&imap->buf.sock, 0, srvc->ssl_verify)) {
1073 + close(s);
1074 + goto bail;
1075 + }
1076 + imap_info( "ok\n" );
1077 }
1078
1079 /* read the greeting string */
@@ -986,7 +1098,18 @@ imap_open_store( imap_server_conf_t *srvc )
1098 goto bail;
1099
1100 if (!preauth) {
989 -
1101 +#ifndef NO_OPENSSL
1102 + if (!srvc->use_ssl && CAP(STARTTLS)) {
1103 + if (imap_exec(ctx, 0, "STARTTLS") != RESP_OK)
1104 + goto bail;
1105 + if (ssl_socket_connect(&imap->buf.sock, 1,
1106 + srvc->ssl_verify))
1107 + goto bail;
1108 + /* capabilities may have changed, so get the new capabilities */
1109 + if (imap_exec(ctx, 0, "CAPABILITY") != RESP_OK)
1110 + goto bail;
1111 + }
1112 +#endif
1113 imap_info ("Logging in...\n");
1114 if (!srvc->user) {
1115 fprintf( stderr, "Skipping server %s, no user\n", srvc->host );
@@ -1014,7 +1137,9 @@ imap_open_store( imap_server_conf_t *srvc )
1137 fprintf( stderr, "Skipping account %s@%s, server forbids LOGIN\n", srvc->user, srvc->host );
1138 goto bail;
1139 }
1017 - imap_warn( "*** IMAP Warning *** Password is being sent in the clear\n" );
1140 + if (!imap->buf.sock.ssl)
1141 + imap_warn( "*** IMAP Warning *** Password is being "
1142 + "sent in the clear\n" );
1143 if (imap_exec( ctx, NULL, "LOGIN \"%s\" \"%s\"", srvc->user, srvc->pass ) != RESP_OK) {
1144 fprintf( stderr, "IMAP error: LOGIN failed\n" );
1145 goto bail;
@@ -1242,6 +1367,8 @@ static imap_server_conf_t server =
1367 0, /* port */
1368 NULL, /* user */
1369 NULL, /* pass */
1370 + 0, /* use_ssl */
1371 + 1, /* ssl_verify */
1372 };
1373
1374 static char *imap_folder;
@@ -1262,11 +1389,11 @@ git_imap_config(const char *key, const char *val, void *cb)
1389 if (!strcmp( "folder", key )) {
1390 imap_folder = xstrdup( val );
1391 } else if (!strcmp( "host", key )) {
1265 - {
1266 - if (!prefixcmp(val, "imap:"))
1267 - val += 5;
1268 - if (!server.port)
1269 - server.port = 143;
1392 + if (!prefixcmp(val, "imap:"))
1393 + val += 5;
1394 + else if (!prefixcmp(val, "imaps:")) {
1395 + val += 6;
1396 + server.use_ssl = 1;
1397 }
1398 if (!prefixcmp(val, "//"))
1399 val += 2;
@@ -1280,6 +1407,8 @@ git_imap_config(const char *key, const char *val, void *cb)
1407 server.port = git_config_int( key, val );
1408 else if (!strcmp( "tunnel", key ))
1409 server.tunnel = xstrdup( val );
1410 + else if (!strcmp( "sslverify", key ))
1411 + server.ssl_verify = git_config_bool( key, val );
1412 return 0;
1413 }
1414
@@ -1300,6 +1429,9 @@ main(int argc, char **argv)
1429 setup_git_directory_gently(&nongit_ok);
1430 git_config(git_imap_config, NULL);
1431
1432 + if (!server.port)
1433 + server.port = server.use_ssl ? 993 : 143;
1434 +
1435 if (!imap_folder) {
1436 fprintf( stderr, "no imap store specified\n" );
1437 return 1;