From 17d6ee994369ec0acf4e30cf74d251522a109890 Mon Sep 17 00:00:00 2001 From: Dan Winship Date: Thu, 1 Mar 2001 22:39:52 +0000 Subject: Add an "authtypes" hash table to CamelImapStore recording the supported * providers/imap/camel-imap-store.c: Add an "authtypes" hash table to CamelImapStore recording the supported authtypes. (connect_to_server): Record supported authtypes in the authtypes hash rather than the capabilities bitmask, since now the IMAP code is no longer responsible for keeping track of which authtypes we support. (query_auth_types): Use camel_sasl_authtype_list to get the SASL-supported authtypes. (try_auth): New function to try a SASL auth mechanism. Sort of formerly imap_try_kerberos_v4_auth. (imap_connect): Get rid of the krb4-specific bits and genericize them for any SASL authtype. * providers/imap/Makefile.am: Remove camel-imap-auth.[ch] (moved into camel-imap-store.c since it's now constant size) and the KRB4_CFLAGS and KRB4_LDFLAGS references. svn path=/trunk/; revision=8463 --- camel/ChangeLog | 17 +++ camel/providers/imap/Makefile.am | 5 +- camel/providers/imap/camel-imap-auth.c | 187 -------------------------------- camel/providers/imap/camel-imap-auth.h | 39 ------- camel/providers/imap/camel-imap-store.c | 182 ++++++++++++++++++++++--------- camel/providers/imap/camel-imap-store.h | 7 +- 6 files changed, 151 insertions(+), 286 deletions(-) delete mode 100644 camel/providers/imap/camel-imap-auth.c delete mode 100644 camel/providers/imap/camel-imap-auth.h diff --git a/camel/ChangeLog b/camel/ChangeLog index 4d17c7e248..f212e25d46 100644 --- a/camel/ChangeLog +++ b/camel/ChangeLog @@ -35,6 +35,23 @@ remove IMAP-isms that shouldn't be in the generic code. This still isn't tested, because we're stuck behind a NAT right now... + * providers/imap/camel-imap-store.c: Add an "authtypes" hash table + to CamelImapStore recording the supported authtypes. + (connect_to_server): Record supported authtypes in the authtypes + hash rather than the capabilities bitmask, since now the IMAP code + is no longer responsible for keeping track of which authtypes we + support. + (query_auth_types): Use camel_sasl_authtype_list to get the + SASL-supported authtypes. + (try_auth): New function to try a SASL auth mechanism. Sort of + formerly imap_try_kerberos_v4_auth. + (imap_connect): Get rid of the krb4-specific bits and genericize + them for any SASL authtype. + + * providers/imap/Makefile.am: Remove camel-imap-auth.[ch] (moved + into camel-imap-store.c since it's now constant size) and the + KRB4_CFLAGS and KRB4_LDFLAGS references. + 2001-03-01 Jeffrey Stedfast * camel-sasl-anonymous.c (anon_challenge): Don't base64 encode the diff --git a/camel/providers/imap/Makefile.am b/camel/providers/imap/Makefile.am index 341fa6b421..39e160c879 100644 --- a/camel/providers/imap/Makefile.am +++ b/camel/providers/imap/Makefile.am @@ -17,11 +17,9 @@ INCLUDES = -I.. \ -I$(top_srcdir) \ -I$(includedir) \ $(GTK_INCLUDEDIR) \ - $(KRB4_CFLAGS) \ -DG_LOG_DOMAIN=\"camel-imap-provider\" libcamelimap_la_SOURCES = \ - camel-imap-auth.c \ camel-imap-command.c \ camel-imap-folder.c \ camel-imap-provider.c \ @@ -32,7 +30,6 @@ libcamelimap_la_SOURCES = \ camel-imap-wrapper.c libcamelimapinclude_HEADERS = \ - camel-imap-auth.h \ camel-imap-command.h \ camel-imap-folder.h \ camel-imap-search.h \ @@ -42,7 +39,7 @@ libcamelimapinclude_HEADERS = \ camel-imap-utils.h \ camel-imap-wrapper.h -libcamelimap_la_LDFLAGS = $(KRB4_LDFLAGS) -version-info 0:0:0 +libcamelimap_la_LDFLAGS = -version-info 0:0:0 noinst_HEADERS = \ camel-imap-private.h diff --git a/camel/providers/imap/camel-imap-auth.c b/camel/providers/imap/camel-imap-auth.c deleted file mode 100644 index a1daf88888..0000000000 --- a/camel/providers/imap/camel-imap-auth.c +++ /dev/null @@ -1,187 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-imap-auth.c: IMAP AUTHENTICATE implementations */ - -/* - * Authors: Dan Winship - * - * Copyright 2000 Helix Code, Inc. (www.helixcode.com) - * - * 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 Street #330, Boston, MA 02111-1307, USA. - * - */ - -#include - -#include -#include - -#include - -#ifdef HAVE_KRB4 -#include -/* MIT krb4 des.h #defines _. Sigh. We don't need it. */ -#undef _ -#endif - -#include "camel-exception.h" -#include "camel-mime-utils.h" - -#include "camel-imap-auth.h" -#include "camel-imap-command.h" -#include "camel-imap-utils.h" - -#include "camel-imap-private.h" - -#ifdef HAVE_KRB4 - -#define IMAP_KERBEROS_V4_PROTECTION_NONE 1 -#define IMAP_KERBEROS_V4_PROTECTION_INTEGRITY 2 -#define IMAP_KERBEROS_V4_PROTECTION_PRIVACY 4 - -gboolean -imap_try_kerberos_v4_auth (CamelImapStore *store, CamelException *ex) -{ - CamelImapResponse *response; - char *resp, *data; - int status, len; - char *inst, *realm, *buf, *username; - guint32 nonce_n, nonce_h, plus1; - struct hostent *h; - KTEXT_ST authenticator; - CREDENTIALS credentials; - des_cblock session; - des_key_schedule schedule; - - CAMEL_IMAP_STORE_LOCK(store, command_lock); - - /* The kickoff. */ - response = camel_imap_command (store, NULL, ex, - "AUTHENTICATE KERBEROS_V4"); - if (!response) - goto fail; - resp = camel_imap_response_extract_continuation (response, ex); - if (!resp) - goto fail; - - data = imap_next_word (resp); - - /* First server response is a base64-encoded 32-bit random number - * ("nonce") in network byte order. - */ - if (strlen (data) != 8 || base64_decode_simple (data, 8) != 4) { - g_free (resp); - goto break_and_lose; - } - memcpy (&nonce_n, data, 4); - g_free (resp); - nonce_h = ntohl (nonce_n); - - /* Our response is an authenticator including that number. */ - h = camel_service_gethost (CAMEL_SERVICE (store), ex); - if (!h) - goto break_and_lose; - inst = g_strndup (h->h_name, strcspn (h->h_name, ".")); - g_strdown (inst); - realm = g_strdup (krb_realmofhost (h->h_name)); - status = krb_mk_req (&authenticator, "imap", inst, realm, nonce_h); - if (status == KSUCCESS) { - status = krb_get_cred ("imap", inst, realm, &credentials); - memcpy (session, credentials.session, sizeof (session)); - memset (&credentials, 0, sizeof (credentials)); - } - g_free (inst); - g_free (realm); - - if (status != KSUCCESS) { - camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Could not get Kerberos ticket:\n%s"), - krb_err_txt[status]); - goto break_and_lose; - } - des_key_sched (&session, schedule); - - buf = base64_encode_simple (authenticator.dat, authenticator.length); - response = camel_imap_command_continuation (store, ex, buf); - g_free (buf); - if (!response) - goto lose; - resp = camel_imap_response_extract_continuation (response, ex); - if (!resp) - goto lose; - data = imap_next_word (resp); - - len = strlen (data); - base64_decode_simple (data, strlen (data)); - - /* This one is encrypted. */ - des_ecb_encrypt ((des_cblock *)data, (des_cblock *)data, schedule, 0); - - /* Check that the returned value is the original nonce plus one. */ - memcpy (&plus1, data, 4); - if (ntohl (plus1) != nonce_h + 1) { - g_free (resp); - goto lose; - } - - /* "the fifth octet contain[s] a bit-mask specifying the - * protection mechanisms supported by the server" - */ - if (!(data[4] & IMAP_KERBEROS_V4_PROTECTION_NONE)) { - g_warning ("Server does not support `no protection' :-("); - g_free (resp); - goto break_and_lose; - } - g_free (resp); - - username = CAMEL_SERVICE (store)->url->user; - len = strlen (username) + 9; - len += 8 - len % 8; - data = g_malloc0 (len); - memcpy (data, &nonce_n, 4); - data[4] = IMAP_KERBEROS_V4_PROTECTION_NONE; - data[5] = data[6] = data[7] = 0; - strcpy (data + 8, username); - - des_pcbc_encrypt ((des_cblock *)data, (des_cblock *)data, len, - schedule, &session, 1); - memset (&session, 0, sizeof (session)); - buf = base64_encode_simple (data, len); - g_free (data); - - response = camel_imap_command_continuation (store, ex, buf); - if (!response) - goto lose; - camel_imap_response_free (response); - CAMEL_IMAP_STORE_UNLOCK(store, command_lock); - return TRUE; - - break_and_lose: - /* Get the server out of "waiting for continuation data" mode. */ - response = camel_imap_command_continuation (store, NULL, "*"); - if (response) - camel_imap_response_free (response); - - lose: - memset (&session, 0, sizeof (session)); - - if (!camel_exception_is_set (ex)) { - camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, - _("Bad authentication response from server.")); - } - fail: - CAMEL_IMAP_STORE_UNLOCK(store, command_lock); - return FALSE; -} -#endif /* HAVE_KRB4 */ diff --git a/camel/providers/imap/camel-imap-auth.h b/camel/providers/imap/camel-imap-auth.h deleted file mode 100644 index fbbc5ef709..0000000000 --- a/camel/providers/imap/camel-imap-auth.h +++ /dev/null @@ -1,39 +0,0 @@ -/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ -/* camel-imap-auth.h: IMAP AUTHENTICATE implementations */ - -/* - * Authors: - * Dan Winship - * - * Copyright (C) 2000 Helix Code, Inc. - * - * 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 - */ - -#ifndef CAMEL_IMAP_AUTH_H -#define CAMEL_IMAP_AUTH_H 1 - -#ifdef __cplusplus -extern "C" { -#pragma } -#endif /* __cplusplus }*/ - -#include "camel-imap-store.h" - -gboolean imap_try_kerberos_v4_auth (CamelImapStore *store, CamelException *ex); -gboolean imap_try_gssapi_auth (CamelImapStore *store, CamelException *ex); - -#endif /* CAMEL_IMAP_AUTH_H */ diff --git a/camel/providers/imap/camel-imap-store.c b/camel/providers/imap/camel-imap-store.c index 960d6275ca..a924c4dab7 100644 --- a/camel/providers/imap/camel-imap-store.c +++ b/camel/providers/imap/camel-imap-store.c @@ -34,7 +34,6 @@ #include #include "camel-imap-store.h" -#include "camel-imap-auth.h" #include "camel-imap-folder.h" #include "camel-imap-utils.h" #include "camel-imap-command.h" @@ -45,6 +44,7 @@ #include "camel-stream-buffer.h" #include "camel-stream-fs.h" #include "camel-url.h" +#include "camel-sasl.h" #include "string-utils.h" #include "camel-imap-private.h" @@ -109,7 +109,7 @@ camel_imap_store_class_init (CamelImapStoreClass *camel_imap_store_class) } static gboolean -free_sub (gpointer key, gpointer value, gpointer user_data) +free_key (gpointer key, gpointer value, gpointer user_data) { g_free (key); return TRUE; @@ -122,9 +122,14 @@ camel_imap_store_finalize (CamelObject *object) if (imap_store->subscribed_folders) { g_hash_table_foreach_remove (imap_store->subscribed_folders, - free_sub, NULL); + free_key, NULL); g_hash_table_destroy (imap_store->subscribed_folders); } + if (imap_store->authtypes) { + g_hash_table_foreach_remove (imap_store->authtypes, + free_key, NULL); + g_hash_table_destroy (imap_store->authtypes); + } if (imap_store->namespace) g_free (imap_store->namespace); #ifdef ENABLE_THREADS @@ -183,8 +188,6 @@ static struct { { "IMAP4REV1", IMAP_CAPABILITY_IMAP4REV1 }, { "STATUS", IMAP_CAPABILITY_STATUS }, { "NAMESPACE", IMAP_CAPABILITY_NAMESPACE }, - { "AUTH=KERBEROS_V4", IMAP_CAPABILITY_AUTH_KERBEROS_V4 }, - { "AUTH=GSSAPI", IMAP_CAPABILITY_AUTH_GSSAPI }, { "UIDPLUS", IMAP_CAPABILITY_UIDPLUS }, { "LITERAL+", IMAP_CAPABILITY_LITERALPLUS }, { NULL, 0 } @@ -214,6 +217,7 @@ connect_to_server (CamelService *service, CamelException *ex) /* Find out the IMAP capabilities */ store->capabilities = 0; + store->authtypes = g_hash_table_new (g_str_hash, g_str_equal); response = camel_imap_command (store, NULL, ex, "CAPABILITY"); if (!response) return FALSE; @@ -226,6 +230,12 @@ connect_to_server (CamelService *service, CamelException *ex) for (capa = strtok_r (capa, " ", &lasts); capa; capa = strtok_r (NULL, " ", &lasts)) { + if (!strncmp (capa, "AUTH=", 5)) { + g_hash_table_insert (store->authtypes, + g_strdup (capa + 5), + GINT_TO_POINTER (1)); + continue; + } for (i = 0; capabilities[i].name; i++) { if (g_strcasecmp (capa, capabilities[i].name) == 0) { store->capabilities |= capabilities[i].flag; @@ -256,39 +266,31 @@ static CamelServiceAuthType password_authtype = { TRUE }; -#ifdef HAVE_KRB4 -static CamelServiceAuthType kerberos_v4_authtype = { - N_("Kerberos 4"), - - N_("This option will connect to the IMAP server using " - "Kerberos 4 authentication."), - - "KERBEROS_V4", - FALSE -}; -#endif - static GList * query_auth_types (CamelService *service, gboolean connect, CamelException *ex) { - GList *types; - + CamelImapStore *store = CAMEL_IMAP_STORE (service); + CamelServiceAuthType *authtype; + GList *types, *sasl_types, *t; + + if (connect && !connect_to_server (service, ex)) + return NULL; + types = CAMEL_SERVICE_CLASS (remote_store_class)->query_auth_types (service, connect, ex); - + + sasl_types = camel_sasl_authtype_list (); if (connect) { - if (!connect_to_server (service, ex)) - return NULL; -#ifdef HAVE_KRB4 - if (CAMEL_IMAP_STORE (service)->capabilities & - IMAP_CAPABILITY_AUTH_KERBEROS_V4) - types = g_list_prepend (types, &kerberos_v4_authtype); -#endif - } else { -#ifdef HAVE_KRB4 - types = g_list_prepend (types, &kerberos_v4_authtype); -#endif + for (t = types; t; t = t->next) { + authtype = t->data; + + if (!g_hash_table_lookup (store->authtypes, authtype->authproto)) { + g_list_remove_link (types, t); + g_list_free_1 (t); + } + } } - + types = g_list_concat (types, sasl_types); + return g_list_prepend (types, &password_authtype); } @@ -317,6 +319,68 @@ imap_store_refresh_folders (CamelRemoteStore *store, CamelException *ex) CAMEL_STORE_UNLOCK(store, cache_lock); } +static gboolean +try_auth (CamelImapStore *store, const char *mech, CamelException *ex) +{ + CamelSasl *sasl; + CamelImapResponse *response; + char *resp; + char *sasl_resp; + + sasl = camel_sasl_new ("imap", mech, CAMEL_SERVICE (store)); + + sasl_resp = camel_sasl_challenge_base64 (sasl, NULL, ex); + + CAMEL_IMAP_STORE_LOCK (store, command_lock); + response = camel_imap_command (store, NULL, ex, "AUTHENTICATE %s%s%s", + mech, sasl_resp ? " " : "", + sasl_resp ? sasl_resp : ""); + if (!response) + goto lose; + + while (!camel_sasl_authenticated (sasl)) { + resp = camel_imap_response_extract_continuation (response, ex); + if (!resp) + goto lose; + + sasl_resp = camel_sasl_challenge_base64 (sasl, resp + 2, ex); + g_free (resp); + if (camel_exception_is_set (ex)) + goto break_and_lose; + + response = camel_imap_command_continuation (store, ex, sasl_resp); + g_free (sasl_resp); + if (!response) + goto lose; + } + + resp = camel_imap_response_extract_continuation (response, NULL); + if (resp) { + /* Oops. SASL claims we're done, but the IMAP server + * doesn't think so... + */ + g_free (resp); + goto lose; + } + + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + return TRUE; + + break_and_lose: + /* Get the server out of "waiting for continuation data" mode. */ + response = camel_imap_command_continuation (store, NULL, "*"); + if (response) + camel_imap_response_free (response); + + lose: + if (!camel_exception_is_set (ex)) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Bad authentication response from server.")); + } + CAMEL_IMAP_STORE_UNLOCK (store, command_lock); + return FALSE; +} + static gboolean imap_connect (CamelService *service, CamelException *ex) { @@ -326,15 +390,13 @@ imap_connect (CamelService *service, CamelException *ex) CamelImapResponse *response; gboolean authenticated = FALSE; int len, i, flags; + CamelServiceAuthType *authtype = NULL; if (connect_to_server (service, ex) == 0) return FALSE; - /* authenticate the user */ -#ifdef HAVE_KRB4 - if (service->url->authmech && - !g_strcasecmp (service->url->authmech, "KERBEROS_V4")) { - if (!(store->capabilities & IMAP_CAPABILITY_AUTH_KERBEROS_V4)) { + if (service->url->authmech) { + if (!g_hash_table_lookup (store->authtypes, service->url->authmech)) { camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, "IMAP server %s does not " "support requested " @@ -345,13 +407,24 @@ imap_connect (CamelService *service, CamelException *ex) return FALSE; } - authenticated = imap_try_kerberos_v4_auth (store, ex); - if (camel_exception_is_set (ex)) { + authtype = camel_sasl_authtype (service->url->authmech); + if (!authtype) { + camel_exception_setv (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + "No support for " + "authentication type %s", + service->url->authmech); camel_service_disconnect (service, TRUE, NULL); return FALSE; } + + if (!authtype->need_password) { + authenticated = try_auth (store, authtype->authproto, ex); + if (!authenticated) { + camel_service_disconnect (service, TRUE, NULL); + return FALSE; + } + } } -#endif while (!authenticated) { if (errbuf) { @@ -363,7 +436,7 @@ imap_connect (CamelService *service, CamelException *ex) service->url->passwd = NULL; } - if (!service->url->authmech && !service->url->passwd) { + if (!service->url->passwd) { char *prompt; prompt = g_strdup_printf (_("%sPlease enter the IMAP " @@ -387,20 +460,25 @@ imap_connect (CamelService *service, CamelException *ex) } } - CAMEL_IMAP_STORE_LOCK(store, command_lock); - response = camel_imap_command (store, NULL, ex, - "LOGIN %S %S", - service->url->user, - service->url->passwd); - CAMEL_IMAP_STORE_UNLOCK(store, command_lock); - if (!response) { + if (authtype) + authenticated = try_auth (store, authtype->authproto, ex); + else { + CAMEL_IMAP_STORE_LOCK(store, command_lock); + response = camel_imap_command (store, NULL, ex, + "LOGIN %S %S", + service->url->user, + service->url->passwd); + CAMEL_IMAP_STORE_UNLOCK(store, command_lock); + if (response) { + camel_imap_response_free (response); + authenticated = TRUE; + } + } + if (!authenticated) { errbuf = g_strdup_printf (_("Unable to authenticate " "to IMAP server.\n%s\n\n"), camel_exception_get_description (ex)); camel_exception_clear (ex); - } else { - authenticated = TRUE; - camel_imap_response_free (response); } } @@ -535,7 +613,7 @@ imap_disconnect (CamelService *service, gboolean clean, CamelException *ex) if (store->subscribed_folders) { g_hash_table_foreach_remove (store->subscribed_folders, - free_sub, NULL); + free_key, NULL); g_hash_table_destroy (store->subscribed_folders); store->subscribed_folders = NULL; } diff --git a/camel/providers/imap/camel-imap-store.h b/camel/providers/imap/camel-imap-store.h index a24723b657..3792e92872 100644 --- a/camel/providers/imap/camel-imap-store.h +++ b/camel/providers/imap/camel-imap-store.h @@ -50,10 +50,8 @@ typedef enum { #define IMAP_CAPABILITY_IMAP4REV1 (1 << 1) #define IMAP_CAPABILITY_STATUS (1 << 2) #define IMAP_CAPABILITY_NAMESPACE (1 << 3) -#define IMAP_CAPABILITY_AUTH_KERBEROS_V4 (1 << 4) -#define IMAP_CAPABILITY_AUTH_GSSAPI (1 << 5) -#define IMAP_CAPABILITY_UIDPLUS (1 << 6) -#define IMAP_CAPABILITY_LITERALPLUS (1 << 7) +#define IMAP_CAPABILITY_UIDPLUS (1 << 4) +#define IMAP_CAPABILITY_LITERALPLUS (1 << 5) struct _CamelImapStore { CamelRemoteStore parent_object; @@ -65,6 +63,7 @@ struct _CamelImapStore { CamelImapServerLevel server_level; guint32 capabilities; + GHashTable *authtypes; char *namespace, dir_sep, *storage_path, *base_url; -- cgit v1.2.3