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;