diff options
author | Thomas Roessler <roessler@does-not-exist.org> | 1999-11-17 19:45:52 +0000 |
---|---|---|
committer | Thomas Roessler <roessler@does-not-exist.org> | 1999-11-17 19:45:52 +0000 |
commit | 114dce60c1c8d53b57fe761de2b201e05cfd07be (patch) | |
tree | cd3cc7183d10263fe2a7a927c87ea5c8ac49873f /imap | |
parent | c42f3d036548d9c814ddb3ab41f5287bbec2b793 (diff) |
Tommi Komulainen's IMAP/SSL support patches.
Diffstat (limited to 'imap')
-rw-r--r-- | imap/Makefile.am | 9 | ||||
-rw-r--r-- | imap/browse.c | 6 | ||||
-rw-r--r-- | imap/imap.c | 26 | ||||
-rw-r--r-- | imap/imap.h | 2 | ||||
-rw-r--r-- | imap/imap_private.h | 1 | ||||
-rw-r--r-- | imap/imap_socket.h | 12 | ||||
-rw-r--r-- | imap/imap_ssl.c | 263 | ||||
-rw-r--r-- | imap/imap_ssl.h | 29 | ||||
-rw-r--r-- | imap/message.c | 4 | ||||
-rw-r--r-- | imap/socket.c | 67 | ||||
-rw-r--r-- | imap/util.c | 37 |
11 files changed, 423 insertions, 33 deletions
diff --git a/imap/Makefile.am b/imap/Makefile.am index 231f1da3..ee2f1f28 100644 --- a/imap/Makefile.am +++ b/imap/Makefile.am @@ -2,12 +2,17 @@ AUTOMAKE_OPTIONS = foreign +if USE_SSL +SSLSOURCES = imap_ssl.c +SSLHEADERS = imap_ssl.h +endif + EXTRA_DIST = BUGS README TODO INCLUDES = -I$(top_srcdir) noinst_LIBRARIES = libimap.a -noinst_HEADERS = imap_private.h imap_socket.h md5.h message.h +noinst_HEADERS = imap_private.h imap_socket.h md5.h message.h $(SSLHEADERS) libimap_a_SOURCES = auth.c browse.c command.c imap.c imap.h md5c.c message.c \ - socket.c util.c + socket.c util.c $(SSLSOURCES) diff --git a/imap/browse.c b/imap/browse.c index f48a9d7e..5769ec33 100644 --- a/imap/browse.c +++ b/imap/browse.c @@ -60,7 +60,7 @@ int imap_init_browse (char *path, struct browser_state *state) int noselect; int noinferiors; - if (imap_parse_path (path, host, sizeof (host), &port, &ipath)) + if (imap_parse_path (path, host, sizeof (host), &port, NULL, &ipath)) { mutt_error ("%s is an invalid IMAP path", path); return -1; @@ -255,7 +255,7 @@ static int add_list_result (CONNECTION *conn, const char *seq, const char *cmd, int noselect; int noinferiors; - if (imap_parse_path (state->folder, host, sizeof (host), &port, &curfolder)) + if (imap_parse_path (state->folder, host, sizeof (host), &port, NULL, &curfolder)) { dprint (2, (debugfile, "add_list_result: current folder %s makes no sense\n", state->folder)); @@ -299,7 +299,7 @@ static void imap_add_folder (char delim, char *folder, int noselect, char *curfolder; int flen = strlen (folder); - if (imap_parse_path (state->folder, host, sizeof (host), &port, &curfolder)) + if (imap_parse_path (state->folder, host, sizeof (host), &port, NULL, &curfolder)) return; imap_unquote_string (folder); diff --git a/imap/imap.c b/imap/imap.c index a9998629..03d1699f 100644 --- a/imap/imap.c +++ b/imap/imap.c @@ -405,7 +405,7 @@ int imap_open_connection (IMAP_DATA *idata, CONNECTION *conn) if (mutt_socket_read_line_d (buf, sizeof (buf), conn) < 0) { - close (conn->fd); + mutt_socket_close_connection (conn); idata->state = IMAP_DISCONNECTED; return (-1); } @@ -415,7 +415,7 @@ int imap_open_connection (IMAP_DATA *idata, CONNECTION *conn) if (imap_check_capabilities(idata) != 0 || imap_authenticate (idata, conn) != 0) { - close (conn->fd); + mutt_socket_close_connection (conn); idata->state = IMAP_DISCONNECTED; return (-1); } @@ -424,7 +424,7 @@ int imap_open_connection (IMAP_DATA *idata, CONNECTION *conn) { if (imap_check_capabilities(idata) != 0) { - close (conn->fd); + mutt_socket_close_connection (conn); idata->state = IMAP_DISCONNECTED; return (-1); } @@ -432,7 +432,7 @@ int imap_open_connection (IMAP_DATA *idata, CONNECTION *conn) else { imap_error ("imap_open_connection()", buf); - close (conn->fd); + mutt_socket_close_connection (conn); idata->state = IMAP_DISCONNECTED; return (-1); } @@ -512,8 +512,9 @@ int imap_open_mailbox (CONTEXT *ctx) int count = 0; int n; int port; + int socktype; - if (imap_parse_path (ctx->path, host, sizeof (host), &port, &pc)) + if (imap_parse_path (ctx->path, host, sizeof (host), &port, &socktype, &pc)) { mutt_error ("%s is an invalid IMAP path", ctx->path); return -1; @@ -530,7 +531,7 @@ int imap_open_mailbox (CONTEXT *ctx) /* We need to create a new connection, the current one isn't useful */ idata = safe_calloc (1, sizeof (IMAP_DATA)); - conn = mutt_socket_select_connection (host, port, M_NEW_SOCKET); + conn = mutt_socket_select_connection (host, port, socktype); conn->data = idata; idata->conn = conn; } @@ -687,7 +688,7 @@ int imap_select_mailbox (CONTEXT* ctx, const char* path) strfcpy (curpath, path, sizeof (curpath)); /* check that the target folder makes sense */ - if (imap_parse_path (curpath, host, sizeof (host), &port, &mbox)) + if (imap_parse_path (curpath, host, sizeof (host), &port, NULL, &mbox)) return -1; /* and that it's on the same server as the current folder */ @@ -736,8 +737,9 @@ int imap_open_mailbox_append (CONTEXT *ctx) char *pc; int r; int port; + int socktype; - if (imap_parse_path (ctx->path, host, sizeof (host), &port, &pc)) + if (imap_parse_path (ctx->path, host, sizeof (host), &port, &socktype, &pc)) return (-1); ctx->magic = M_IMAP; @@ -831,7 +833,7 @@ int imap_close_connection (CONTEXT *ctx) while (mutt_strncmp (seq, buf, SEQLEN) != 0); mutt_clear_error (); } - close (CTX_DATA->conn->fd); + mutt_socket_close_connection (CTX_DATA->conn); CTX_DATA->state = IMAP_DISCONNECTED; CTX_DATA->conn->uses = 0; CTX_DATA->conn->data = NULL; @@ -1159,7 +1161,7 @@ int imap_mailbox_check (char *path, int new) int msgcount = 0; int port; - if (imap_parse_path (path, host, sizeof (host), &port, &pc)) + if (imap_parse_path (path, host, sizeof (host), &port, NULL, &pc)) return -1; conn = mutt_socket_select_connection (host, port, 0); @@ -1344,7 +1346,7 @@ int imap_subscribe (char *path, int subscribe) char *ipath = NULL; int port; - if (imap_parse_path (path, host, sizeof (host), &port, &ipath)) + if (imap_parse_path (path, host, sizeof (host), &port, NULL, &ipath)) return (-1); conn = mutt_socket_select_connection (host, port, 0); @@ -1399,7 +1401,7 @@ int imap_complete(char* dest, size_t dlen, char* path) { int pos = 0; /* verify passed in path is an IMAP path */ - if (imap_parse_path (path, host, sizeof(host), &port, &mbox)) + if (imap_parse_path (path, host, sizeof(host), &port, NULL, &mbox)) { dprint(2, (debugfile, "imap_complete: bad path %s\n", path)); return -1; diff --git a/imap/imap.h b/imap/imap.h index 1afecf4f..978fe4bd 100644 --- a/imap/imap.h +++ b/imap/imap.h @@ -45,7 +45,7 @@ int imap_fetch_message (MESSAGE* msg, CONTEXT* ctx, int msgno); /* util.c */ int imap_parse_path (char* path, char* host, size_t hlen, int* port, - char** mbox); + int *socktype, char** mbox); void imap_qualify_path (char* dest, size_t len, const char* host, int port, const char* path, const char* name); diff --git a/imap/imap_private.h b/imap/imap_private.h index 6a5c3fe7..62b7a58c 100644 --- a/imap/imap_private.h +++ b/imap/imap_private.h @@ -25,6 +25,7 @@ /* -- symbols -- */ #define IMAP_PORT 143 +#define IMAP_SSL_PORT 993 /* number of entries in the hash table */ #define IMAP_CACHE_LEN 10 diff --git a/imap/imap_socket.h b/imap/imap_socket.h index 5b99114d..3fd9d24e 100644 --- a/imap/imap_socket.h +++ b/imap/imap_socket.h @@ -32,14 +32,26 @@ typedef struct _connection int available; void *data; struct _connection *next; + + void *sockdata; + int (*read) (struct _connection *conn); + int (*write) (struct _connection *conn, const char *buf); + int (*open) (struct _connection *conn); + int (*close) (struct _connection *conn); } CONNECTION; + int mutt_socket_readchar (CONNECTION *conn, char *c); int mutt_socket_read_line (char *buf, size_t buflen, CONNECTION *conn); int mutt_socket_read_line_d (char *buf, size_t buflen, CONNECTION *conn); int mutt_socket_write (CONNECTION *conn, const char *buf); CONNECTION *mutt_socket_select_connection (char *host, int port, int flags); int mutt_socket_open_connection (CONNECTION *conn); +int mutt_socket_close_connection (CONNECTION *conn); +int raw_socket_read (CONNECTION *conn); +int raw_socket_write (CONNECTION *conn, const char *buf); +int raw_socket_open (CONNECTION *conn); +int raw_socket_close (CONNECTION *conn); #endif /* _IMAP_SOCKET_H_ */ diff --git a/imap/imap_ssl.c b/imap/imap_ssl.c new file mode 100644 index 00000000..e6b99c8b --- /dev/null +++ b/imap/imap_ssl.c @@ -0,0 +1,263 @@ +/* + * Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include <openssl/ssl.h> +#include <openssl/x509.h> +#include <openssl/err.h> + +#undef _ + +#include <string.h> + +#include "mutt.h" +#include "imap_socket.h" +#include "mutt_menu.h" +#include "mutt_curses.h" +#include "imap_ssl.h" + + +char *SslCertFile = NULL; + +typedef struct _sslsockdata +{ + SSL_CTX *ctx; + SSL *ssl; + X509 *cert; +} +sslsockdata; + + +static int ssl_check_certificate (sslsockdata * data); + + +int ssl_socket_read (CONNECTION * conn) +{ + sslsockdata *data = conn->sockdata; + return SSL_read (data->ssl, conn->inbuf, LONG_STRING); +} + +int ssl_socket_write (CONNECTION * conn, const char *buf) +{ + sslsockdata *data = conn->sockdata; + dprint (1, (debugfile, "ssl_socket_write():%s", buf)); + return SSL_write (data->ssl, buf, mutt_strlen (buf)); +} + +int ssl_socket_open (CONNECTION * conn) +{ + sslsockdata *data; + int err; + + if (raw_socket_open (conn) < 0) + return -1; + + data = (sslsockdata *) safe_calloc (1, sizeof (sslsockdata)); + conn->sockdata = data; + + SSLeay_add_ssl_algorithms (); + data->ctx = SSL_CTX_new (SSLv23_client_method ()); + + data->ssl = SSL_new (data->ctx); + SSL_set_fd (data->ssl, conn->fd); + + if ((err = SSL_connect (data->ssl)) < 0) + { + ssl_socket_close (conn); + return -1; + } + + data->cert = SSL_get_peer_certificate (data->ssl); + if (!data->cert) + { + mutt_error (_("Unable to get certificate from peer")); + sleep (1); + return -1; + } + + if (!ssl_check_certificate (data)) + { + ssl_socket_close (conn); + return -1; + } + + mutt_message (_("SSL connection using %s"), SSL_get_cipher (data->ssl)); + sleep (1); + + return 0; +} + +int ssl_socket_close (CONNECTION * conn) +{ + sslsockdata *data = conn->sockdata; + SSL_shutdown (data->ssl); + + X509_free (data->cert); + SSL_free (data->ssl); + SSL_CTX_free (data->ctx); + + return raw_socket_close (conn); +} + + + +static char *x509_get_part (char *line, const char *ndx) +{ + static char ret[SHORT_STRING]; + char *c, *c2; + + strncpy (ret, _("Unknown"), sizeof (ret)); + + c = strstr (line, ndx); + if (c) + { + c += strlen (ndx); + c2 = strchr (c, '/'); + if (c2) + *c2 = '\0'; + strncpy (ret, c, sizeof (ret)); + if (c2) + *c2 = '/'; + } + + return ret; +} + +static void x509_fingerprint (char *s, int l, X509 * cert) +{ + unsigned char md[EVP_MAX_MD_SIZE]; + unsigned int n; + int j; + + if (!X509_digest (cert, EVP_md5 (), md, &n)) + { + snprintf (s, l, _("[unable to calculate]")); + } + else + { + for (j = 0; j < (int) n; j++) + { + char ch[8]; + snprintf (ch, 8, "%02X%s", md[j], (j % 2 ? " " : "")); + strncat (s, ch, l); + } + } +} + + +static int ssl_check_certificate (sslsockdata * data) +{ + char *part[] = + {"/CN=", "/Email=", "/O=", "/OU=", "/L=", "/ST=", "/C="}; + char helpstr[SHORT_STRING]; + char buf[SHORT_STRING]; + MUTTMENU *menu; + int done, i; + FILE *fp; + char *line = NULL, *c; + + /* automatic check from user's database */ + fp = fopen (SslCertFile, "rt"); + if (fp) + { + EVP_PKEY *peer = X509_get_pubkey (data->cert); + X509 *savedkey = NULL; + int pass = 0; + while ((savedkey = PEM_read_X509 (fp, &savedkey, NULL))) + { + if (X509_verify (savedkey, peer)) + { + pass = 1; + break; + } + } + fclose (fp); + if (pass) + return 1; + } + + menu = mutt_new_menu (); + menu->max = 15; + menu->dialog = (char **) safe_calloc (1, menu->max * sizeof (char *)); + for (i = 0; i < menu->max; i++) + menu->dialog[i] = (char *) safe_calloc (1, SHORT_STRING * sizeof (char)); + + strncpy (menu->dialog[0], _("This certificate belongs to:"), SHORT_STRING); + line = X509_NAME_oneline (X509_get_subject_name (data->cert), + buf, sizeof (buf)); + for (i = 1; i <= 5; i++) + { + c = x509_get_part (line, part[i - 1]); + snprintf (menu->dialog[i], SHORT_STRING, " %s", c); + } + + strncpy (menu->dialog[7], _("This certificate was issued by:"), SHORT_STRING); + line = X509_NAME_oneline (X509_get_subject_name (data->cert), + buf, sizeof (buf)); + for (i = 8; i <= 12; i++) + { + c = x509_get_part (line, part[i - 8]); + snprintf (menu->dialog[i], SHORT_STRING, " %s", c); + } + + buf[0] = '\0'; + x509_fingerprint (buf, sizeof (buf), data->cert); + snprintf (menu->dialog[14], SHORT_STRING, _("Fingerprint: %s"), buf); + + menu->title = _("SSL Certificate check"); + menu->prompt = _("(r)eject, accept (o)nce, (a)ccept always"); + menu->keys = _("roa"); + + helpstr[0] = '\0'; + mutt_make_help (buf, sizeof (buf), _("Exit "), MENU_GENERIC, OP_EXIT); + strncat (helpstr, buf, sizeof (helpstr)); + mutt_make_help (buf, sizeof (buf), _("Help"), MENU_GENERIC, OP_HELP); + strncat (helpstr, buf, sizeof (helpstr)); + menu->help = helpstr; + + done = 0; + while (!done) + { + switch (mutt_menuLoop (menu)) + { + case -1: /* abort */ + case OP_MAX + 1: /* reject */ + case OP_EXIT: + done = 1; + break; + case OP_MAX + 3: /* accept always */ + done = 0; + fp = fopen (SslCertFile, "w+t"); + if (fp) + { + if (PEM_write_X509 (fp, data->cert)) + done = 1; + fclose (fp); + } + if (!done) + mutt_error (_("Warning: Couldn't save certificate")); + else + mutt_message (_("Certificate saved")); + sleep (1); + /* fall through */ + case OP_MAX + 2: /* accept once */ + done = 2; + break; + } + } + mutt_menuDestroy (&menu); + return (done == 2); +} diff --git a/imap/imap_ssl.h b/imap/imap_ssl.h new file mode 100644 index 00000000..7e56d40d --- /dev/null +++ b/imap/imap_ssl.h @@ -0,0 +1,29 @@ +/* + * Copyright (C) 1999 Tommi Komulainen <Tommi.Komulainen@iki.fi> + * + * 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., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef _MUTT_SSL_H_ +#define _MUTT_SSL_H_ 1 + +extern char *SslCertFile; + +extern int ssl_socket_read (CONNECTION *conn); +extern int ssl_socket_write (CONNECTION *conn, const char *buf); +extern int ssl_socket_open (CONNECTION *conn); +extern int ssl_socket_close (CONNECTION *conn); + +#endif diff --git a/imap/message.c b/imap/message.c index 9b143259..1b7c16c1 100644 --- a/imap/message.c +++ b/imap/message.c @@ -404,7 +404,7 @@ int imap_append_message (CONTEXT *ctx, MESSAGE *msg) size_t len; int c, last; - if (imap_parse_path (ctx->path, host, sizeof (host), &port, &pc)) + if (imap_parse_path (ctx->path, host, sizeof (host), &port, NULL, &pc)) return (-1); imap_fix_path (CTX_DATA, pc, mailbox, sizeof (mailbox)); @@ -526,7 +526,7 @@ int imap_copy_messages (CONTEXT* ctx, HEADER* h, char* dest, int delete) int rc; int n; - if (imap_parse_path (dest, host, sizeof (host), &port, &pc)) + if (imap_parse_path (dest, host, sizeof (host), &port, NULL, &pc)) { dprint (1, (debugfile, "imap_copy_message: bad destination %s\n", dest)); return -1; diff --git a/imap/socket.c b/imap/socket.c index bbf1605b..a81bfa38 100644 --- a/imap/socket.c +++ b/imap/socket.c @@ -19,6 +19,9 @@ #include "mutt.h" #include "globals.h" #include "imap_socket.h" +#ifdef USE_SSL +#include "imap_ssl.h" +#endif #include <unistd.h> #include <netinet/in.h> @@ -32,12 +35,32 @@ static CONNECTION *Connections = NULL; + +/* Wrappers */ +int mutt_socket_open_connection (CONNECTION *conn) +{ + return conn->open (conn); +} + +int mutt_socket_close_connection (CONNECTION *conn) +{ + return conn->close (conn); +} + +int mutt_socket_write (CONNECTION *conn, const char *buf) +{ + dprint (1,(debugfile,"> %s", buf)); + return conn->write (conn, buf); +} + + + /* simple read buffering to speed things up. */ int mutt_socket_readchar (CONNECTION *conn, char *c) { if (conn->bufpos >= conn->available) { - conn->available = read (conn->fd, conn->inbuf, LONG_STRING); + conn->available = conn->read (conn); conn->bufpos = 0; if (conn->available <= 0) return conn->available; /* returns 0 for EOF or -1 for other error */ @@ -74,17 +97,15 @@ int mutt_socket_read_line_d (char *buf, size_t buflen, CONNECTION *conn) return r; } -int mutt_socket_write (CONNECTION *conn, const char *buf) -{ - dprint (1,(debugfile,"> %s", buf)); - return (write (conn->fd, buf, mutt_strlen (buf))); -} - CONNECTION *mutt_socket_select_connection (char *host, int port, int flags) { CONNECTION *conn; +#ifdef USE_SSL + if (flags != M_NEW_SOCKET && flags != M_NEW_SSL_SOCKET) +#else if (flags != M_NEW_SOCKET) +#endif { conn = Connections; while (conn) @@ -104,6 +125,21 @@ CONNECTION *mutt_socket_select_connection (char *host, int port, int flags) conn->next = Connections; Connections = conn; + conn->read = raw_socket_read; + conn->write = raw_socket_write; + conn->open = raw_socket_open; + conn->close = raw_socket_close; + +#ifdef USE_SSL + if (flags == M_NEW_SSL_SOCKET) + { + conn->read = ssl_socket_read; + conn->write = ssl_socket_write; + conn->open = ssl_socket_open; + conn->close = ssl_socket_close; + } +#endif + return conn; } @@ -131,7 +167,22 @@ static int try_socket_and_connect (CONNECTION *conn, struct sockaddr_in sin, return 0; } -int mutt_socket_open_connection (CONNECTION *conn) +int raw_socket_close (CONNECTION *conn) +{ + return close (conn->fd); +} + +int raw_socket_read (CONNECTION *conn) +{ + return read (conn->fd, conn->inbuf, LONG_STRING); +} + +int raw_socket_write (CONNECTION *conn, const char *buf) +{ + return write (conn->fd, buf, mutt_strlen (buf)); +} + +int raw_socket_open (CONNECTION *conn) { struct sockaddr_in sin; struct hostent *he; diff --git a/imap/util.c b/imap/util.c index 5e6d7f71..b02dde96 100644 --- a/imap/util.c +++ b/imap/util.c @@ -121,20 +121,22 @@ char *imap_next_word (char *s) /* imap_parse_path: given an IMAP mailbox name, return host, port * and a path IMAP servers will recognise. */ int imap_parse_path (char* path, char* host, size_t hlen, int* port, - char** mbox) + int *socktype, char** mbox) { int n; char *pc; char *pt; /* set default port */ - *port = IMAP_PORT; + *port = 0; + if (socktype) + *socktype = M_NEW_SOCKET; pc = path; if (*pc != '{') return -1; pc++; n = 0; - while (*pc && *pc != '}' && *pc != ':' && (n < hlen-1)) + while (*pc && *pc != '}' && *pc != ':' && *pc != '/' && (n < hlen-1)) host[n++] = *pc++; host[n] = 0; /* catch NULL hosts */ @@ -147,11 +149,13 @@ int imap_parse_path (char* path, char* host, size_t hlen, int* port, return -1; if (*pc == ':') { + char c; pc++; pt = pc; - while (*pc && *pc != '}') pc++; + while (*pc && *pc != '}' && *pc != '/') pc++; if (!*pc) return -1; + c = *pc; *pc = '\0'; *port = atoi (pt); if (!port) @@ -159,10 +163,33 @@ int imap_parse_path (char* path, char* host, size_t hlen, int* port, dprint (1, (debugfile, "imap_parse_path: bad port in %s\n", path)); return -1; } + *pc = c; + } + if (*pc == '/') + { + pc++; + pt = pc; + while (*pc && *pc != '}') pc++; + if (!*pc) + return (-1); + *pc = '\0'; +#ifdef USE_SSL + if (!strcmp (pt, "ssl")) + { + if (socktype) + *socktype = M_NEW_SSL_SOCKET; + if (!*port) + *port = IMAP_SSL_PORT; + } else +#endif + return (-1); *pc = '}'; } pc++; - + + if (!*port) + *port = IMAP_PORT; + *mbox = pc; return 0; } |