/*
* Authors: Jeffrey Stedfast <fejj@ximian.com>
*
- * Copyright 2003 Ximian, Inc. (www.ximian.com)
+ * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
*
* You should have received a copy of the GNU Lesser 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.
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*
*/
+/* If building without Kerberos support, this class is an empty shell. */
#ifdef HAVE_CONFIG_H
#include <config.h>
#include <errno.h>
-#ifdef HAVE_KRB5
-#include <netdb.h>
#include <string.h>
-#include <sys/socket.h>
#include <sys/types.h>
+
+#ifndef _WIN32
+#include <netdb.h>
+#include <sys/socket.h>
+#endif
+
+#include <gio/gio.h>
+#include <glib/gi18n-lib.h>
+
+#include "camel-net-utils.h"
+#include "camel-network-settings.h"
+#include "camel-sasl-gssapi.h"
+#include "camel-session.h"
+
+#ifdef HAVE_KRB5
+
+#ifdef HAVE_HEIMDAL_KRB5
+#include <krb5.h>
+#else
+#include <krb5/krb5.h>
+#endif /* HAVE_HEIMDAL_KRB5 */
+
#ifdef HAVE_ET_COM_ERR_H
#include <et/com_err.h>
#else
+#ifdef HAVE_COM_ERR_H
#include <com_err.h>
-#endif
+#endif /* HAVE_COM_ERR_H */
+#endif /* HAVE_ET_COM_ERR_H */
+
#ifdef HAVE_MIT_KRB5
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_generic.h>
-#endif
+#endif /* HAVE_MIT_KRB5 */
+
#ifdef HAVE_HEIMDAL_KRB5
#include <gssapi.h>
-#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
#else
-#ifdef HAVE_SUN_KRB5
+#ifdef HAVE_SUN_KRB5
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_ext.h>
extern gss_OID gss_nt_service_name;
-#endif
-#endif
+#endif /* HAVE_SUN_KRB5 */
+#endif /* HAVE_HEIMDAL_KRB5 */
+
+#define CAMEL_SASL_GSSAPI_GET_PRIVATE(obj) \
+ (G_TYPE_INSTANCE_GET_PRIVATE \
+ ((obj), CAMEL_TYPE_SASL_GSSAPI, CamelSaslGssapiPrivate))
#ifndef GSS_C_OID_KRBV5_DES
#define GSS_C_OID_KRBV5_DES GSS_C_NO_OID
#endif
-#include <glib/gi18n.h>
-#include <glib/gi18n-lib.h>
-
-#include "camel-net-utils.h"
-#include "camel-sasl-gssapi.h"
+#define DBUS_PATH "/org/gnome/KrbAuthDialog"
+#define DBUS_INTERFACE "org.gnome.KrbAuthDialog"
+#define DBUS_METHOD "org.gnome.KrbAuthDialog.acquireTgt"
-CamelServiceAuthType camel_sasl_gssapi_authtype = {
+static CamelServiceAuthType sasl_gssapi_auth_type = {
N_("GSSAPI"),
-
+
N_("This option will connect to the server using "
"Kerberos 5 authentication."),
-
+
"GSSAPI",
FALSE
};
#define DESIRED_SECURITY_LAYER GSSAPI_SECURITY_LAYER_NONE
struct _CamelSaslGssapiPrivate {
- int state;
+ gint state;
gss_ctx_id_t ctx;
gss_name_t target;
};
+#endif /* HAVE_KRB5 */
-static GByteArray *gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex);
-
-
-static CamelSaslClass *parent_class = NULL;
-
+G_DEFINE_TYPE (CamelSaslGssapi, camel_sasl_gssapi, CAMEL_TYPE_SASL)
-static void
-camel_sasl_gssapi_class_init (CamelSaslGssapiClass *klass)
-{
- CamelSaslClass *camel_sasl_class = CAMEL_SASL_CLASS (klass);
-
- parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ()));
-
- /* virtual method overload */
- camel_sasl_class->challenge = gssapi_challenge;
-}
-
-static void
-camel_sasl_gssapi_init (gpointer object, gpointer klass)
-{
- CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
-
- gssapi->priv = g_new (struct _CamelSaslGssapiPrivate, 1);
- gssapi->priv->state = GSSAPI_STATE_INIT;
- gssapi->priv->ctx = GSS_C_NO_CONTEXT;
- gssapi->priv->target = GSS_C_NO_NAME;
-}
+#ifdef HAVE_KRB5
static void
-camel_sasl_gssapi_finalize (CamelObject *object)
-{
- CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
- guint32 status;
-
- if (gssapi->priv->ctx != GSS_C_NO_CONTEXT)
- gss_delete_sec_context (&status, &gssapi->priv->ctx, GSS_C_NO_BUFFER);
-
- if (gssapi->priv->target != GSS_C_NO_NAME)
- gss_release_name (&status, &gssapi->priv->target);
-
- g_free (gssapi->priv);
-}
-
-
-CamelType
-camel_sasl_gssapi_get_type (void)
+gssapi_set_exception (OM_uint32 major,
+ OM_uint32 minor,
+ GError **error)
{
- static CamelType type = CAMEL_INVALID_TYPE;
-
- if (type == CAMEL_INVALID_TYPE) {
- type = camel_type_register (
- camel_sasl_get_type (),
- "CamelSaslGssapi",
- sizeof (CamelSaslGssapi),
- sizeof (CamelSaslGssapiClass),
- (CamelObjectClassInitFunc) camel_sasl_gssapi_class_init,
- NULL,
- (CamelObjectInitFunc) camel_sasl_gssapi_init,
- (CamelObjectFinalizeFunc) camel_sasl_gssapi_finalize);
- }
-
- return type;
-}
+ const gchar *str;
-static void
-gssapi_set_exception (OM_uint32 major, OM_uint32 minor, CamelException *ex)
-{
- const char *str;
-
switch (major) {
case GSS_S_BAD_MECH:
str = _("The specified mechanism is not supported by the "
default:
str = _("Bad authentication response from server.");
}
-
- camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, str);
+
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+ "%s", str);
+}
+
+static void
+sasl_gssapi_finalize (GObject *object)
+{
+ CamelSaslGssapi *sasl = CAMEL_SASL_GSSAPI (object);
+ guint32 status;
+
+ if (sasl->priv->ctx != GSS_C_NO_CONTEXT)
+ gss_delete_sec_context (
+ &status, &sasl->priv->ctx, GSS_C_NO_BUFFER);
+
+ if (sasl->priv->target != GSS_C_NO_NAME)
+ gss_release_name (&status, &sasl->priv->target);
+
+ /* Chain up to parent's finalize() method. */
+ G_OBJECT_CLASS (camel_sasl_gssapi_parent_class)->finalize (object);
+}
+
+/* DBUS Specific code */
+
+static gboolean
+send_dbus_message (const gchar *name)
+{
+ gint success = FALSE;
+ GError *error = NULL;
+ GDBusConnection *connection;
+ GDBusMessage *message, *reply;
+
+ connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
+ if (error) {
+ g_warning ("could not get system bus: %s\n", error->message);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ g_dbus_connection_set_exit_on_close (connection, FALSE);
+
+ /* Create a new message on the DBUS_INTERFACE */
+ message = g_dbus_message_new_method_call (DBUS_INTERFACE, DBUS_PATH, DBUS_INTERFACE, "acquireTgt");
+ if (!message) {
+ g_object_unref (connection);
+ return FALSE;
+ }
+
+ /* Appends the data as an argument to the message */
+ if (strchr (name, '\\'))
+ name = strchr (name, '\\');
+ g_dbus_message_set_body (message, g_variant_new ("(s)", name));
+
+ /* Sends the message: Have a 300 sec wait timeout */
+ reply = g_dbus_connection_send_message_with_reply_sync (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, 300 * 1000, NULL, NULL, &error);
+
+ if (!error && reply) {
+ if (g_dbus_message_to_gerror (reply, &error)) {
+ g_object_unref (reply);
+ reply = NULL;
+ }
+ }
+
+ if (error) {
+ g_dbus_error_strip_remote_error (error);
+ g_warning ("%s: %s\n", G_STRFUNC, error->message);
+ g_error_free (error);
+ }
+
+ if (reply) {
+ GVariant *body = g_dbus_message_get_body (reply);
+
+ if (body)
+ g_variant_get (body, "(b)", &success);
+
+ g_object_unref (reply);
+ }
+
+ /* Free the message */
+ g_object_unref (message);
+ g_object_unref (connection);
+
+ return success;
}
+/* END DBus stuff */
+
static GByteArray *
-gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex)
+sasl_gssapi_challenge_sync (CamelSasl *sasl,
+ GByteArray *token,
+ GCancellable *cancellable,
+ GError **error)
{
- struct _CamelSaslGssapiPrivate *priv = CAMEL_SASL_GSSAPI (sasl)->priv;
+ CamelSaslGssapiPrivate *priv;
+ CamelNetworkSettings *network_settings;
+ CamelSettings *settings;
+ CamelService *service;
OM_uint32 major, minor, flags, time;
gss_buffer_desc inbuf, outbuf;
GByteArray *challenge = NULL;
gss_buffer_t input_token;
- int conf_state;
+ gint conf_state;
gss_qop_t qop;
gss_OID mech;
- char *str;
+ gchar *str;
struct addrinfo *ai, hints;
-
+ const gchar *service_name;
+ gchar *host;
+ gchar *user;
+
+ priv = CAMEL_SASL_GSSAPI_GET_PRIVATE (sasl);
+
+ service = camel_sasl_get_service (sasl);
+ service_name = camel_sasl_get_service_name (sasl);
+
+ settings = camel_service_ref_settings (service);
+ g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), NULL);
+
+ network_settings = CAMEL_NETWORK_SETTINGS (settings);
+ host = camel_network_settings_dup_host (network_settings);
+ user = camel_network_settings_dup_user (network_settings);
+
+ g_object_unref (settings);
+
+ g_return_val_if_fail (user != NULL, NULL);
+
+ if (host == NULL)
+ host = g_strdup ("localhost");
+
switch (priv->state) {
case GSSAPI_STATE_INIT:
- memset(&hints, 0, sizeof(hints));
+ memset (&hints, 0, sizeof (hints));
hints.ai_flags = AI_CANONNAME;
- ai = camel_getaddrinfo(sasl->service->url->host?sasl->service->url->host:"localhost", NULL, &hints, ex);
+ ai = camel_getaddrinfo (
+ host, NULL, &hints, cancellable, error);
if (ai == NULL)
- return NULL;
-
- str = g_strdup_printf("%s@%s", sasl->service_name, ai->ai_canonname);
- camel_freeaddrinfo(ai);
-
+ goto exit;
+
+ str = g_strdup_printf("%s@%s", service_name, ai->ai_canonname);
+ camel_freeaddrinfo (ai);
+
inbuf.value = str;
inbuf.length = strlen (str);
- major = gss_import_name (&minor, &inbuf, gss_nt_service_name, &priv->target);
+ major = gss_import_name (&minor, &inbuf, GSS_C_NT_HOSTBASED_SERVICE, &priv->target);
g_free (str);
-
+
if (major != GSS_S_COMPLETE) {
- gssapi_set_exception (major, minor, ex);
- return NULL;
+ gssapi_set_exception (major, minor, error);
+ goto exit;
}
-
+
input_token = GSS_C_NO_BUFFER;
-
+
goto challenge;
break;
case GSSAPI_STATE_CONTINUE_NEEDED:
if (token == NULL) {
- camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
- _("Bad authentication response from server."));
- return NULL;
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+ _("Bad authentication response from server."));
+ goto exit;
}
-
+
inbuf.value = token->data;
inbuf.length = token->len;
input_token = &inbuf;
-
+
challenge:
major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target,
GSS_C_OID_KRBV5_DES, GSS_C_MUTUAL_FLAG |
GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
0, GSS_C_NO_CHANNEL_BINDINGS,
input_token, &mech, &outbuf, &flags, &time);
-
+
switch (major) {
case GSS_S_COMPLETE:
priv->state = GSSAPI_STATE_COMPLETE;
priv->state = GSSAPI_STATE_CONTINUE_NEEDED;
break;
default:
- gssapi_set_exception (major, minor, ex);
- return NULL;
+ if (major == (OM_uint32) GSS_S_FAILURE &&
+ (minor == (OM_uint32) KRB5KRB_AP_ERR_TKT_EXPIRED ||
+ minor == (OM_uint32) KRB5KDC_ERR_NEVER_VALID) &&
+ send_dbus_message (user))
+ goto challenge;
+
+ gssapi_set_exception (major, minor, error);
+ goto exit;
}
-
+
challenge = g_byte_array_new ();
g_byte_array_append (challenge, outbuf.value, outbuf.length);
#ifndef HAVE_HEIMDAL_KRB5
break;
case GSSAPI_STATE_COMPLETE:
if (token == NULL) {
- camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
- _("Bad authentication response from server."));
- return NULL;
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+ _("Bad authentication response from server."));
+ goto exit;
}
-
+
inbuf.value = token->data;
inbuf.length = token->len;
-
+
major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop);
if (major != GSS_S_COMPLETE) {
- gssapi_set_exception (major, minor, ex);
- return NULL;
+ gssapi_set_exception (major, minor, error);
+ goto exit;
}
-
+
if (outbuf.length < 4) {
- camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
- _("Bad authentication response from server."));
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+ _("Bad authentication response from server."));
#ifndef HAVE_HEIMDAL_KRB5
gss_release_buffer (&minor, &outbuf);
#endif
- return NULL;
+ goto exit;
}
-
+
/* check that our desired security layer is supported */
- if ((((unsigned char *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
- camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
- _("Unsupported security layer."));
+ if ((((guchar *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
+ g_set_error (
+ error, CAMEL_SERVICE_ERROR,
+ CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
+ _("Unsupported security layer."));
#ifndef HAVE_HEIMDAL_KRB5
gss_release_buffer (&minor, &outbuf);
#endif
- return NULL;
+ goto exit;
}
-
- inbuf.length = 4 + strlen (sasl->service->url->user);
+
+ inbuf.length = 4 + strlen (user);
inbuf.value = str = g_malloc (inbuf.length);
memcpy (inbuf.value, outbuf.value, 4);
str[0] = DESIRED_SECURITY_LAYER;
- memcpy (str + 4, sasl->service->url->user, inbuf.length - 4);
-
+ memcpy (str + 4, user, inbuf.length - 4);
+
#ifndef HAVE_HEIMDAL_KRB5
gss_release_buffer (&minor, &outbuf);
#endif
-
+
major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
if (major != GSS_S_COMPLETE) {
- gssapi_set_exception (major, minor, ex);
+ gssapi_set_exception (major, minor, error);
g_free (str);
- return NULL;
+ goto exit;
}
-
+
g_free (str);
challenge = g_byte_array_new ();
g_byte_array_append (challenge, outbuf.value, outbuf.length);
-
+
#ifndef HAVE_HEIMDAL_KRB5
gss_release_buffer (&minor, &outbuf);
#endif
-
+
priv->state = GSSAPI_STATE_AUTHENTICATED;
-
- sasl->authenticated = TRUE;
+
+ camel_sasl_set_authenticated (sasl, TRUE);
break;
default:
- return NULL;
+ break;
}
-
+
+exit:
+ g_free (host);
+ g_free (user);
+
return challenge;
}
#endif /* HAVE_KRB5 */
+
+static void
+camel_sasl_gssapi_class_init (CamelSaslGssapiClass *class)
+{
+#ifdef HAVE_KRB5
+ GObjectClass *object_class;
+ CamelSaslClass *sasl_class;
+
+ g_type_class_add_private (class, sizeof (CamelSaslGssapiPrivate));
+
+ object_class = G_OBJECT_CLASS (class);
+ object_class->finalize = sasl_gssapi_finalize;
+
+ sasl_class = CAMEL_SASL_CLASS (class);
+ sasl_class->auth_type = &sasl_gssapi_auth_type;
+ sasl_class->challenge_sync = sasl_gssapi_challenge_sync;
+#endif /* HAVE_KRB5 */
+}
+
+static void
+camel_sasl_gssapi_init (CamelSaslGssapi *sasl)
+{
+#ifdef HAVE_KRB5
+ sasl->priv = CAMEL_SASL_GSSAPI_GET_PRIVATE (sasl);
+ sasl->priv->state = GSSAPI_STATE_INIT;
+ sasl->priv->ctx = GSS_C_NO_CONTEXT;
+ sasl->priv->target = GSS_C_NO_NAME;
+#endif /* HAVE_KRB5 */
+}