From b6089a2dcb082acd71e0cfd4c9d75b96db2e0b83 Mon Sep 17 00:00:00 2001 From: Michael Elkins Date: Thu, 21 Dec 2000 06:27:05 +0000 Subject: [PATCH] added OpenSSL support --- configure.in | 2 + imap.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++----- isync.1 | 32 +++++++++++--- isync.h | 23 +++++++++- main.c | 75 ++++++++++++++++++++++++-------- 5 files changed, 214 insertions(+), 36 deletions(-) diff --git a/configure.in b/configure.in index 9587290..50cbc58 100644 --- a/configure.in +++ b/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) diff --git a/imap.c b/imap.c index 1b1ec31..e05c59f 100644 --- a/imap.c +++ b/imap.c @@ -29,6 +29,9 @@ #include #include #include +#if HAVE_LIBSSL +#include +#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); diff --git a/isync.1 b/isync.1 index 7f2beed..a29e376 100644 --- a/isync.1 +++ b/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 . -.. -.SH BUGS -SSL is currently not used when connecting to the IMAP server. A future -version of -.B isync -is expected to support this. diff --git a/isync.h b/isync.h index 3545593..8c92044 100644 --- a/isync.h +++ b/isync.h @@ -19,10 +19,22 @@ */ #include +#if HAVE_LIBSSL +#include +#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); diff --git a/main.c b/main.c index fc27aec..aa5cb9a 100644 --- a/main.c +++ b/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);