From b3eb5661eb70979f12deb88c5f732014a5f933e8 Mon Sep 17 00:00:00 2001 From: Michael Elkins Date: Sat, 23 Dec 2000 00:02:42 +0000 Subject: [PATCH] added CRAM-MD5 authentication support. parse server capability string to determine if STARTTLS is available --- Makefile.am | 2 +- README | 3 +- cram.c | 85 +++++++++++++++++++++++++++++++++++++++++++++++++++++ imap.c | 74 +++++++++++++++++++++++++++++++++++++++++----- isync.h | 7 +++++ 5 files changed, 161 insertions(+), 10 deletions(-) create mode 100644 cram.c diff --git a/Makefile.am b/Makefile.am index 22734f4..bbe8dd5 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ bin_PROGRAMS=isync -isync_SOURCES=main.c imap.c sync.c maildir.c isync.h list.c +isync_SOURCES=main.c imap.c sync.c maildir.c isync.h list.c cram.c man_MANS=isync.1 EXTRA_DIST=sample.isyncrc $(man_MANS) CPPFLAGS=$(RPM_OPT_FLAGS) diff --git a/README b/README index bccbdce..ec0df10 100644 --- a/README +++ b/README @@ -18,8 +18,9 @@ maintained, and all flags are synchronized. * Fast mode for fetching new mail only * Supports imaps: (port 993) TLS/SSL connections - * Supports STARTTLS (RFC2595) + * Supports STARTTLS (RFC2595) for confidentiality * Supports NAMESPACE (RFC2342) + * Supports CRAM-MD5 (RFC2095) for authentication * Compatibility diff --git a/cram.c b/cram.c new file mode 100644 index 0000000..99b63b7 --- /dev/null +++ b/cram.c @@ -0,0 +1,85 @@ +/* $Id$ + * + * isync - IMAP4 to maildir mailbox synchronizer + * Copyright (C) 2000 Michael R. Elkins + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include +#include "isync.h" + +#if HAVE_LIBSSL + +#include + +#define ENCODED_SIZE(n) (4*((n+2)/3)) + +static char +hexchar (unsigned int b) +{ + if (b < 10) + return '0' + b; + return 'a' + (b - 10); +} + +char * +cram (const char *challenge, const char *user, const char *pass) +{ + HMAC_CTX hmac; + char hash[16]; + char hex[33]; + int i; + unsigned int hashlen = sizeof (hash); + char buf[256]; + int len = strlen (challenge); + char *response = calloc (1, 1 + len); + char *final; + + /* response will always be smaller than challenge because we are + * decoding. + */ + len = EVP_DecodeBlock ((unsigned char *) response, (unsigned char *) challenge, strlen (challenge)); +// printf ("CRAM-MD5 challege is %s\n", response); + + HMAC_Init (&hmac, (unsigned char *) pass, strlen (pass), EVP_md5 ()); + HMAC_Update (&hmac, (unsigned char *) response, strlen(response)); + HMAC_Final (&hmac, (unsigned char *) hash, &hashlen); + + assert (hashlen == sizeof (hash)); + + free (response); + + hex[32] = 0; + for (i = 0; i < 16; i++) + { + hex[2 * i] = hexchar ((hash[i] >> 4) & 0xf); + hex[2 * i + 1] = hexchar (hash[i] & 0xf); + } + + snprintf (buf, sizeof (buf), "%s %s", user, hex); +// printf ("Response: %s\n", buf); + + len = strlen (buf); + len = ENCODED_SIZE (len) + 1; + final = malloc (len); + final[len - 1] = 0; + + assert (EVP_EncodeBlock ((unsigned char *) final, (unsigned char *) buf, strlen (buf)) == len - 1); + + return final; +} + +#endif diff --git a/imap.c b/imap.c index 1ec5c08..7bcc2b2 100644 --- a/imap.c +++ b/imap.c @@ -401,6 +401,18 @@ imap_exec (imap_t * imap, const char *fmt, ...) { parse_response_code (imap, cmd); } + else if (!strcmp ("CAPABILITY", arg)) + { +#if HAVE_LIBSSL + while ((arg = next_arg (&cmd))) + { + if (!strcmp ("STARTTLS", arg)) + imap->have_starttls = 1; + else if (!strcmp ("AUTH=CRAM-MD5", arg)) + imap->have_cram = 1; + } +#endif + } else if ((arg1 = next_arg (&cmd))) { if (!strcmp ("EXISTS", arg1)) @@ -428,6 +440,26 @@ imap_exec (imap_t * imap, const char *fmt, ...) return -1; } } +#if HAVE_LIBSSL + else if (*arg == '+') + { + char *resp; + + if (!imap->cram) + { + puts ("Error, not doing CRAM-MD5 authentication"); + return -1; + } + resp = cram (cmd, imap->box->user, imap->box->pass); + + socket_write (imap->sock, resp, strlen (resp)); + if (Verbose) + puts (resp); + socket_write (imap->sock, "\r\n", 2); + free (resp); + imap->cram = 0; + } +#endif else if ((size_t) atol (arg) != Tag) { puts ("wrong tag"); @@ -510,17 +542,23 @@ imap_open (config_t * box, unsigned int minuid) #if HAVE_LIBSSL if (!box->use_imaps) { + /* let's see what this puppy can do... */ + ret = imap_exec (imap, "CAPABILITY"); + /* always try to select SSL support if available */ - ret = imap_exec (imap, "STARTTLS"); - if (!ret) + if (imap->have_starttls && !imap_exec (imap, "STARTTLS")) use_ssl = 1; - else if (box->require_ssl) + + if (!use_ssl) { - puts ("Error, SSL support not available"); - return 0; + if (box->require_ssl) + { + puts ("Error, SSL support not available"); + return 0; + } + else + puts ("Warning, SSL support not available"); } - else - puts ("Warning, SSL support not available"); } else use_ssl = 1; @@ -543,11 +581,31 @@ imap_open (config_t * box, unsigned int minuid) imap->sock->use_ssl = 1; puts ("SSL support enabled"); + + if (box->use_imaps) + ret = imap_exec (imap, "CAPABILITY"); } +#else + ret = imap_exec (imap, "CAPABILITY"); #endif puts ("Logging in..."); - ret = imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user, box->pass); +#if HAVE_LIBSSL + if (imap->have_cram) + { + puts ("Authenticating with CRAM-MD5"); + imap->cram = 1; + ret = imap_exec (imap, "AUTHENTICATE CRAM-MD5"); + } + else +#endif + { +#if HAVE_LIBSSL + if (!use_ssl) +#endif + puts ("*** Warning *** Password is being sent in the clear"); + ret = imap_exec (imap, "LOGIN \"%s\" \"%s\"", box->user, box->pass); + } if (!ret) { diff --git a/isync.h b/isync.h index d175fe8..6fa2837 100644 --- a/isync.h +++ b/isync.h @@ -130,6 +130,11 @@ typedef struct list_t *ns_personal; list_t *ns_other; list_t *ns_shared; +#if HAVE_LIBSSL + unsigned int have_cram:1; + unsigned int have_starttls:1; + unsigned int cram:1; +#endif } imap_t; @@ -144,6 +149,8 @@ extern int Verbose; #if HAVE_LIBSSL extern SSL_CTX *SSLContext; + +char *cram (const char *, const char *, const char *); #endif char *next_arg (char **);