1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright (C) 1999-2008 Novell, Inc. (www.novell.com)
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU Lesser General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
23 /* If building without Kerberos support, this class is an empty shell. */
32 #include <sys/types.h>
36 #include <sys/socket.h>
40 #include <glib/gi18n-lib.h>
42 #include "camel-net-utils.h"
43 #include "camel-network-settings.h"
44 #include "camel-sasl-gssapi.h"
45 #include "camel-session.h"
49 #ifdef HAVE_HEIMDAL_KRB5
52 #include <krb5/krb5.h>
53 #endif /* HAVE_HEIMDAL_KRB5 */
55 #ifdef HAVE_ET_COM_ERR_H
56 #include <et/com_err.h>
60 #endif /* HAVE_COM_ERR_H */
61 #endif /* HAVE_ET_COM_ERR_H */
64 #include <gssapi/gssapi.h>
65 #include <gssapi/gssapi_generic.h>
66 #endif /* HAVE_MIT_KRB5 */
68 #ifdef HAVE_HEIMDAL_KRB5
72 #include <gssapi/gssapi.h>
73 #include <gssapi/gssapi_ext.h>
74 extern gss_OID gss_nt_service_name;
75 #endif /* HAVE_SUN_KRB5 */
76 #endif /* HAVE_HEIMDAL_KRB5 */
78 #define CAMEL_SASL_GSSAPI_GET_PRIVATE(obj) \
79 (G_TYPE_INSTANCE_GET_PRIVATE \
80 ((obj), CAMEL_TYPE_SASL_GSSAPI, CamelSaslGssapiPrivate))
82 #ifndef GSS_C_OID_KRBV5_DES
83 #define GSS_C_OID_KRBV5_DES GSS_C_NO_OID
86 #define DBUS_PATH "/org/gnome/KrbAuthDialog"
87 #define DBUS_INTERFACE "org.gnome.KrbAuthDialog"
88 #define DBUS_METHOD "org.gnome.KrbAuthDialog.acquireTgt"
90 static CamelServiceAuthType sasl_gssapi_auth_type = {
93 N_("This option will connect to the server using "
94 "Kerberos 5 authentication."),
102 GSSAPI_STATE_CONTINUE_NEEDED,
103 GSSAPI_STATE_COMPLETE,
104 GSSAPI_STATE_AUTHENTICATED
107 #define GSSAPI_SECURITY_LAYER_NONE (1 << 0)
108 #define GSSAPI_SECURITY_LAYER_INTEGRITY (1 << 1)
109 #define GSSAPI_SECURITY_LAYER_PRIVACY (1 << 2)
111 #define DESIRED_SECURITY_LAYER GSSAPI_SECURITY_LAYER_NONE
113 struct _CamelSaslGssapiPrivate {
119 #endif /* HAVE_KRB5 */
121 G_DEFINE_TYPE (CamelSaslGssapi, camel_sasl_gssapi, CAMEL_TYPE_SASL)
126 gssapi_set_exception (OM_uint32 major,
134 str = _("The specified mechanism is not supported by the "
135 "provided credential, or is unrecognized by the "
139 str = _("The provided target_name parameter was ill-formed.");
141 case GSS_S_BAD_NAMETYPE:
142 str = _("The provided target_name parameter contained an "
143 "invalid or unsupported type of name.");
145 case GSS_S_BAD_BINDINGS:
146 str = _("The input_token contains different channel "
147 "bindings to those specified via the "
148 "input_chan_bindings parameter.");
151 str = _("The input_token contains an invalid signature, or a "
152 "signature that could not be verified.");
155 str = _("The supplied credentials were not valid for context "
156 "initiation, or the credential handle did not "
157 "reference any credentials.");
159 case GSS_S_NO_CONTEXT:
160 str = _("The supplied context handle did not refer to a valid context.");
162 case GSS_S_DEFECTIVE_TOKEN:
163 str = _("The consistency checks performed on the input_token failed.");
165 case GSS_S_DEFECTIVE_CREDENTIAL:
166 str = _("The consistency checks performed on the credential failed.");
168 case GSS_S_CREDENTIALS_EXPIRED:
169 str = _("The referenced credentials have expired.");
172 str = error_message (minor);
175 str = _("Bad authentication response from server.");
179 error, CAMEL_SERVICE_ERROR,
180 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
185 sasl_gssapi_finalize (GObject *object)
187 CamelSaslGssapi *sasl = CAMEL_SASL_GSSAPI (object);
190 if (sasl->priv->ctx != GSS_C_NO_CONTEXT)
191 gss_delete_sec_context (
192 &status, &sasl->priv->ctx, GSS_C_NO_BUFFER);
194 if (sasl->priv->target != GSS_C_NO_NAME)
195 gss_release_name (&status, &sasl->priv->target);
197 /* Chain up to parent's finalize() method. */
198 G_OBJECT_CLASS (camel_sasl_gssapi_parent_class)->finalize (object);
201 /* DBUS Specific code */
204 send_dbus_message (const gchar *name)
206 gint success = FALSE;
207 GError *error = NULL;
208 GDBusConnection *connection;
209 GDBusMessage *message, *reply;
211 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
213 g_warning ("could not get system bus: %s\n", error->message);
214 g_error_free (error);
219 g_dbus_connection_set_exit_on_close (connection, FALSE);
221 /* Create a new message on the DBUS_INTERFACE */
222 message = g_dbus_message_new_method_call (DBUS_INTERFACE, DBUS_PATH, DBUS_INTERFACE, "acquireTgt");
224 g_object_unref (connection);
228 /* Appends the data as an argument to the message */
229 if (strchr (name, '\\'))
230 name = strchr (name, '\\');
231 g_dbus_message_set_body (message, g_variant_new ("(s)", name));
233 /* Sends the message: Have a 300 sec wait timeout */
234 reply = g_dbus_connection_send_message_with_reply_sync (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, 300 * 1000, NULL, NULL, &error);
236 if (!error && reply) {
237 if (g_dbus_message_to_gerror (reply, &error)) {
238 g_object_unref (reply);
244 g_dbus_error_strip_remote_error (error);
245 g_warning ("%s: %s\n", G_STRFUNC, error->message);
246 g_error_free (error);
250 GVariant *body = g_dbus_message_get_body (reply);
253 g_variant_get (body, "(b)", &success);
255 g_object_unref (reply);
258 /* Free the message */
259 g_object_unref (message);
260 g_object_unref (connection);
268 sasl_gssapi_challenge_sync (CamelSasl *sasl,
270 GCancellable *cancellable,
273 CamelSaslGssapiPrivate *priv;
274 CamelNetworkSettings *network_settings;
275 CamelSettings *settings;
276 CamelService *service;
277 OM_uint32 major, minor, flags, time;
278 gss_buffer_desc inbuf, outbuf;
279 GByteArray *challenge = NULL;
280 gss_buffer_t input_token;
285 struct addrinfo *ai, hints;
286 const gchar *service_name;
290 priv = CAMEL_SASL_GSSAPI_GET_PRIVATE (sasl);
292 service = camel_sasl_get_service (sasl);
293 service_name = camel_sasl_get_service_name (sasl);
295 settings = camel_service_ref_settings (service);
296 g_return_val_if_fail (CAMEL_IS_NETWORK_SETTINGS (settings), NULL);
298 network_settings = CAMEL_NETWORK_SETTINGS (settings);
299 host = camel_network_settings_dup_host (network_settings);
300 user = camel_network_settings_dup_user (network_settings);
302 g_object_unref (settings);
304 g_return_val_if_fail (user != NULL, NULL);
307 host = g_strdup ("localhost");
309 switch (priv->state) {
310 case GSSAPI_STATE_INIT:
311 memset (&hints, 0, sizeof (hints));
312 hints.ai_flags = AI_CANONNAME;
313 ai = camel_getaddrinfo (
314 host, NULL, &hints, cancellable, error);
318 str = g_strdup_printf("%s@%s", service_name, ai->ai_canonname);
319 camel_freeaddrinfo (ai);
322 inbuf.length = strlen (str);
323 major = gss_import_name (&minor, &inbuf, GSS_C_NT_HOSTBASED_SERVICE, &priv->target);
326 if (major != GSS_S_COMPLETE) {
327 gssapi_set_exception (major, minor, error);
331 input_token = GSS_C_NO_BUFFER;
335 case GSSAPI_STATE_CONTINUE_NEEDED:
338 error, CAMEL_SERVICE_ERROR,
339 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
340 _("Bad authentication response from server."));
344 inbuf.value = token->data;
345 inbuf.length = token->len;
346 input_token = &inbuf;
349 major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target,
350 GSS_C_OID_KRBV5_DES, GSS_C_MUTUAL_FLAG |
351 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
352 0, GSS_C_NO_CHANNEL_BINDINGS,
353 input_token, &mech, &outbuf, &flags, &time);
357 priv->state = GSSAPI_STATE_COMPLETE;
359 case GSS_S_CONTINUE_NEEDED:
360 priv->state = GSSAPI_STATE_CONTINUE_NEEDED;
363 if (major == (OM_uint32) GSS_S_FAILURE &&
364 (minor == (OM_uint32) KRB5KRB_AP_ERR_TKT_EXPIRED ||
365 minor == (OM_uint32) KRB5KDC_ERR_NEVER_VALID) &&
366 send_dbus_message (user))
369 gssapi_set_exception (major, minor, error);
373 challenge = g_byte_array_new ();
374 g_byte_array_append (challenge, outbuf.value, outbuf.length);
375 #ifndef HAVE_HEIMDAL_KRB5
376 gss_release_buffer (&minor, &outbuf);
379 case GSSAPI_STATE_COMPLETE:
382 error, CAMEL_SERVICE_ERROR,
383 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
384 _("Bad authentication response from server."));
388 inbuf.value = token->data;
389 inbuf.length = token->len;
391 major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop);
392 if (major != GSS_S_COMPLETE) {
393 gssapi_set_exception (major, minor, error);
397 if (outbuf.length < 4) {
399 error, CAMEL_SERVICE_ERROR,
400 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
401 _("Bad authentication response from server."));
402 #ifndef HAVE_HEIMDAL_KRB5
403 gss_release_buffer (&minor, &outbuf);
408 /* check that our desired security layer is supported */
409 if ((((guchar *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
411 error, CAMEL_SERVICE_ERROR,
412 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
413 _("Unsupported security layer."));
414 #ifndef HAVE_HEIMDAL_KRB5
415 gss_release_buffer (&minor, &outbuf);
420 inbuf.length = 4 + strlen (user);
421 inbuf.value = str = g_malloc (inbuf.length);
422 memcpy (inbuf.value, outbuf.value, 4);
423 str[0] = DESIRED_SECURITY_LAYER;
424 memcpy (str + 4, user, inbuf.length - 4);
426 #ifndef HAVE_HEIMDAL_KRB5
427 gss_release_buffer (&minor, &outbuf);
430 major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
431 if (major != GSS_S_COMPLETE) {
432 gssapi_set_exception (major, minor, error);
438 challenge = g_byte_array_new ();
439 g_byte_array_append (challenge, outbuf.value, outbuf.length);
441 #ifndef HAVE_HEIMDAL_KRB5
442 gss_release_buffer (&minor, &outbuf);
445 priv->state = GSSAPI_STATE_AUTHENTICATED;
447 camel_sasl_set_authenticated (sasl, TRUE);
460 #endif /* HAVE_KRB5 */
463 camel_sasl_gssapi_class_init (CamelSaslGssapiClass *class)
466 GObjectClass *object_class;
467 CamelSaslClass *sasl_class;
469 g_type_class_add_private (class, sizeof (CamelSaslGssapiPrivate));
471 object_class = G_OBJECT_CLASS (class);
472 object_class->finalize = sasl_gssapi_finalize;
474 sasl_class = CAMEL_SASL_CLASS (class);
475 sasl_class->auth_type = &sasl_gssapi_auth_type;
476 sasl_class->challenge_sync = sasl_gssapi_challenge_sync;
477 #endif /* HAVE_KRB5 */
481 camel_sasl_gssapi_init (CamelSaslGssapi *sasl)
484 sasl->priv = CAMEL_SASL_GSSAPI_GET_PRIVATE (sasl);
485 sasl->priv->state = GSSAPI_STATE_INIT;
486 sasl->priv->ctx = GSS_C_NO_CONTEXT;
487 sasl->priv->target = GSS_C_NO_NAME;
488 #endif /* HAVE_KRB5 */