Przeglądaj źródła

added OpenSSL support

Michael Elkins 24 lat temu
rodzic
commit
b6089a2dcb
5 zmienionych plików z 214 dodań i 36 usunięć
  1. 2 0
      configure.in
  2. 108 10
      imap.c
  3. 25 7
      isync.1
  4. 22 1
      isync.h
  5. 57 18
      main.c

+ 2 - 0
configure.in

@@ -5,5 +5,7 @@ if test $CC = gcc; then
 	CFLAGS="$CFLAGS -pipe"
 fi
 AC_CHECK_FUNCS(getopt_long)
+AC_CHECK_LIB(crypto,ERR_error_string)
+AC_CHECK_LIB(ssl,SSL_library_init)
 CFLAGS="$CFLAGS -W -Wall -pedantic -Wmissing-prototypes -Wmissing-declarations"
 AC_OUTPUT(Makefile)

+ 108 - 10
imap.c

@@ -29,6 +29,9 @@
 #include <netinet/in.h>
 #include <arpa/inet.h>
 #include <netdb.h>
+#if HAVE_LIBSSL
+#include <openssl/err.h>
+#endif
 #include "isync.h"
 
 const char *Flags[] = {
@@ -40,6 +43,56 @@ const char *Flags[] = {
     "\\Draft"
 };
 
+#if HAVE_LIBSSL
+SSL_CTX *SSLContext = 0;
+
+static int
+init_ssl (config_t * conf)
+{
+    if (!conf->cert_file)
+    {
+	puts ("Error, CertificateFile not defined");
+	return -1;
+    }
+    SSL_library_init ();
+    SSL_load_error_strings ();
+    SSLContext = SSL_CTX_new (SSLv23_client_method ());
+    if (!SSL_CTX_load_verify_locations (SSLContext, conf->cert_file, NULL))
+    {
+	printf ("Error, SSL_CTX_load_verify_locations: %s\n",
+		ERR_error_string (ERR_get_error (), 0));
+	return -1;
+    }
+    SSL_CTX_set_verify (SSLContext,
+			SSL_VERIFY_PEER |
+			SSL_VERIFY_FAIL_IF_NO_PEER_CERT |
+			SSL_VERIFY_CLIENT_ONCE, NULL);
+    SSL_CTX_set_verify_depth (SSLContext, 1);
+    return 0;
+}
+
+#endif
+
+static int
+socket_read (Socket_t * sock, char *buf, size_t len)
+{
+#if HAVE_LIBSSL
+    if (sock->use_ssl)
+	return SSL_read (sock->ssl, buf, len);
+#endif
+    return read (sock->fd, buf, len);
+}
+
+static int
+socket_write (Socket_t * sock, char *buf, size_t len)
+{
+#if HAVE_LIBSSL
+    if (sock->use_ssl)
+	return SSL_write (sock->ssl, buf, len);
+#endif
+    return write (sock->fd, buf, len);
+}
+
 /* simple line buffering */
 static int
 buffer_gets (buffer_t * b, char **s)
@@ -64,7 +117,10 @@ buffer_gets (buffer_t * b, char **s)
 	    b->offset = n;
 	    start = 0;
 
-	    n = read (b->fd, b->buf + b->offset, sizeof (b->buf) - b->offset);
+	    n =
+		socket_read (b->sock, b->buf + b->offset,
+			     sizeof (b->buf) - b->offset);
+
 	    if (n <= 0)
 	    {
 		if (n == -1)
@@ -112,7 +168,7 @@ imap_exec (imap_t * imap, const char *fmt, ...)
     snprintf (buf, sizeof (buf), "%d %s\r\n", ++Tag, tmp);
     if (Verbose)
 	fputs (buf, stdout);
-    write (imap->fd, buf, strlen (buf));
+    socket_write (imap->sock, buf, strlen (buf));
 
     for (;;)
     {
@@ -223,7 +279,6 @@ imap_exec (imap_t * imap, const char *fmt, ...)
 	    arg = next_arg (&cmd);
 	    if (!strcmp ("OK", arg))
 		return 0;
-	    puts ("IMAP command failed");
 	    return -1;
 	}
     }
@@ -289,6 +344,15 @@ imap_open (config_t * box, int fast)
     int s;
     struct sockaddr_in sin;
     struct hostent *he;
+#if HAVE_LIBSSL
+    int use_ssl = 0;
+#endif
+
+#if HAVE_LIBSSL
+    /* initialize SSL */
+    if (init_ssl (box))
+	return 0;
+#endif
 
     /* open connection to IMAP server */
 
@@ -321,12 +385,46 @@ imap_open (config_t * box, int fast)
     puts ("ok");
 
     imap = calloc (1, sizeof (imap_t));
-    imap->fd = s;
-    //imap->state = imap_state_init;
+    imap->sock = calloc (1, sizeof (Socket_t));
+    imap->sock->fd = s;
     imap->buf = calloc (1, sizeof (buffer_t));
-    imap->buf->fd = s;
+    imap->buf->sock = imap->sock;
     imap->box = box;
 
+#if HAVE_LIBSSL
+    if (!box->use_imaps)
+    {
+	/* always try to select SSL support if available */
+	ret = imap_exec (imap, "STARTTLS");
+	if (!ret)
+	    use_ssl = 1;
+	else if (box->require_ssl)
+	{
+	    puts ("Error, SSL support not available");
+	    return 0;
+	}
+	else
+	    puts ("Warning, SSL support not available");
+    }
+    else
+	use_ssl = 1;
+
+    if (use_ssl)
+    {
+	imap->sock->ssl = SSL_new (SSLContext);
+	SSL_set_fd (imap->sock->ssl, imap->sock->fd);
+	ret = SSL_connect (imap->sock->ssl);
+	if (ret <= 0)
+	{
+	    ret = SSL_get_error (imap->sock->ssl, ret);
+	    printf ("Error, SSL_connect: %s\n", ERR_error_string (ret, 0));
+	    return 0;
+	}
+	imap->sock->use_ssl = 1;
+	puts ("SSL support enabled");
+    }
+#endif
+
     puts ("Logging in...");
     ret = imap_exec (imap, "LOGIN %s %s", box->user, box->pass);
     if (!ret)
@@ -395,7 +493,7 @@ write_strip (int fd, char *buf, size_t len)
 }
 
 static void
-send_server (int fd, const char *fmt, ...)
+send_server (Socket_t * sock, const char *fmt, ...)
 {
     char buf[128];
     char cmd[128];
@@ -406,7 +504,7 @@ send_server (int fd, const char *fmt, ...)
     va_end (ap);
 
     snprintf (cmd, sizeof (cmd), "%d %s\r\n", ++Tag, buf);
-    write (fd, cmd, strlen (cmd));
+    socket_write (sock, cmd, strlen (cmd));
 
     if (Verbose)
 	fputs (cmd, stdout);
@@ -421,7 +519,7 @@ imap_fetch_message (imap_t * imap, unsigned int uid, int fd)
     size_t n;
     char buf[1024];
 
-    send_server (imap->fd, "UID FETCH %d RFC822.PEEK", uid);
+    send_server (imap->sock, "UID FETCH %d RFC822.PEEK", uid);
 
     for (;;)
     {
@@ -477,7 +575,7 @@ imap_fetch_message (imap_t * imap, unsigned int uid, int fd)
 		n = bytes;
 		if (n > sizeof (buf))
 		    n = sizeof (buf);
-		n = read (imap->fd, buf, n);
+		n = socket_read (imap->sock, buf, n);
 		if (n > 0)
 		{
 //                  printf("imap_fetch_message:%d:read %d bytes\n", __LINE__, n);

+ 25 - 7
isync.1

@@ -115,7 +115,19 @@ command, apply to this mailbox only.
 ..
 .TP
 \fBHost\fR \fIname\fR
-Defines the DNS name or IP address of the IMAP server
+Defines the DNS name or IP address of the IMAP server.  If the hostname is
+prefixed with
+.I imaps:
+the connection is assumed to be a SSL connection to port 993 (though you can
+change this by placing a
+.B Port
+command
+.B after
+the
+.B Host
+command.  Note that some servers support SSL on the default port 143.
+.B isync
+will always attempt to use SSL if available.
 ..
 .TP
 \fBPort\fR \fIport\fR
@@ -144,6 +156,18 @@ will prompt you for it.
 \fBAlias\fR \fIstring\fR
 Defines an alias for the mailbox which can be used as a shortcut on the
 command line.
+..
+.TP
+\fBRequireSSL\fR \fIyes|no\fR
+.B isync will abort the connection if a TLS/SSL session to the IMAP
+server can not be established.  (Default:
+.I yes
+)
+..
+.TP
+\fBCertificateFile\fR \fIpath\fR
+File containing X.509 CA certificates used to verify server identities.
+..
 .P
 Configuration commands that appear prior to the first
 .B Mailbox
@@ -176,9 +200,3 @@ http://www.sigpipe.org/isync/.
 ..
 .SH AUTHOR
 Written by Michael R. Elkins <me@mutt.org>.
-..
-.SH BUGS
-SSL is currently not used when connecting to the IMAP server.  A future
-version of
-.B isync
-is expected to support this.

+ 22 - 1
isync.h

@@ -19,10 +19,22 @@
  */
 
 #include <stdarg.h>
+#if HAVE_LIBSSL
+#include <openssl/ssl.h>
+#endif
 
 typedef struct
 {
     int fd;
+#if HAVE_LIBSSL
+    SSL *ssl;
+    unsigned int use_ssl:1;
+#endif
+} Socket_t;
+
+typedef struct
+{
+    Socket_t *sock;
     char buf[1024];
     int bytes;
     int offset;
@@ -43,6 +55,11 @@ struct config
     char *box;
     char *alias;
     config_t *next;
+#if HAVE_LIBSSL
+    char *cert_file;
+    unsigned int use_imaps:1;
+    unsigned int require_ssl:1;
+#endif
 };
 
 /* struct representing local mailbox file */
@@ -78,7 +95,7 @@ struct message
 /* imap connection info */
 typedef struct
 {
-    int fd;			/* server socket */
+    Socket_t *sock;
     unsigned int count;		/* # of msgs */
     unsigned int recent;	/* # of recent messages */
     buffer_t *buf;		/* input buffer for reading server output */
@@ -101,6 +118,10 @@ extern unsigned int Tag;
 extern char Hostname[256];
 extern int Verbose;
 
+#if HAVE_LIBSSL
+extern SSL_CTX *SSLContext;
+#endif
+
 char *next_arg (char **);
 
 int sync_mailbox (mailbox_t *, imap_t *, int);

+ 57 - 18
main.c

@@ -107,6 +107,22 @@ enter_password (void)
     return strdup (pass);
 }
 
+/* set defaults from the global configuration section */
+static void
+config_defaults (config_t * conf)
+{
+    conf->user = global.user;
+    conf->pass = global.pass;
+    conf->port = global.port;
+    conf->box = global.box;
+    conf->host = global.host;
+#if HAVE_LIBSSL
+    conf->require_ssl = global.require_ssl;
+    conf->use_imaps = global.use_imaps;
+    conf->cert_file = global.cert_file;
+#endif
+}
+
 static void
 load_config (char *where)
 {
@@ -152,10 +168,25 @@ load_config (char *where)
 	    if (*cur)
 		cur = &(*cur)->next;
 	    *cur = calloc (1, sizeof (config_t));
+	    config_defaults (*cur);
 	    (*cur)->path = strdup (p);
 	}
 	else if (!strncasecmp ("host", buf, 4))
 	{
+	    if (!strncasecmp ("imaps:", p, 6))
+	    {
+		p += 6;
+		if (*cur)
+		{
+		    (*cur)->use_imaps = 1;
+		    (*cur)->port = 993;
+		}
+		else
+		{
+		    global.use_imaps = 1;
+		    global.port = 993;
+		}
+	    }
 	    if (*cur)
 		(*cur)->host = strdup (p);
 	    else
@@ -194,6 +225,22 @@ load_config (char *where)
 	    if (*cur)
 		(*cur)->alias = strdup (p);
 	}
+#if HAVE_LIBSSL
+	else if (!strncasecmp ("CertificateFile", buf, 15))
+	{
+	    if (*cur)
+		(*cur)->cert_file = strdup (p);
+	    else
+		global.cert_file = strdup (p);
+	}
+	else if (!strncasecmp ("RequireSSL", buf, 10))
+	{
+	    if (*cur)
+		(*cur)->require_ssl = (strcasecmp (p, "yes") == 0);
+	    else
+		global.require_ssl = (strcasecmp (p, "yes") == 0);
+	}
+#endif
 	else if (buf[0])
 	    printf ("%s:%d:unknown command:%s", path, line, buf);
     }
@@ -257,6 +304,12 @@ main (int argc, char **argv)
     global.port = 143;
     global.box = "INBOX";
     global.user = strdup (pw->pw_name);
+#if HAVE_LIBSSL
+    /* this will probably annoy people, but its the best default just in
+     * case people forget to turn it on
+     */
+    global.require_ssl = 1;
+#endif
 
 #if HAVE_GETOPT_LONG
     while ((i = getopt_long (argc, argv, "defhp:u:r:s:vV", Opts, NULL)) != -1)
@@ -326,29 +379,15 @@ main (int argc, char **argv)
 	box = &global;
     }
 
-    /* fill in missing info with defaults */
     if (!box->pass)
     {
-	if (!global.pass)
+	box->pass = enter_password ();
+	if (!box->pass)
 	{
-	    box->pass = enter_password ();
-	    if (!box->pass)
-	    {
-		puts ("Aborting, no password");
-		exit (1);
-	    }
+	    puts ("Aborting, no password");
+	    exit (1);
 	}
-	else
-	    box->pass = global.pass;
     }
-    if (!box->user)
-	box->user = global.user;
-    if (!box->port)
-	box->port = global.port;
-    if (!box->host)
-	box->host = global.host;
-    if (!box->box)
-	box->box = global.box;
 
     printf ("Reading %s\n", box->path);
     mail = maildir_open (box->path, fast);