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.
32 #include <sys/socket.h>
33 #include <sys/types.h>
34 #ifdef HAVE_HEIMDAL_KRB5
37 #include <krb5/krb5.h>
39 #ifdef HAVE_ET_COM_ERR_H
40 #include <et/com_err.h>
47 #include <gssapi/gssapi.h>
48 #include <gssapi/gssapi_generic.h>
50 #ifdef HAVE_HEIMDAL_KRB5
54 #include <gssapi/gssapi.h>
55 #include <gssapi/gssapi_ext.h>
56 extern gss_OID gss_nt_service_name;
60 #ifndef GSS_C_OID_KRBV5_DES
61 #define GSS_C_OID_KRBV5_DES GSS_C_NO_OID
64 #include <glib/gi18n-lib.h>
67 #include "camel-net-utils.h"
68 #include "camel-sasl-gssapi.h"
69 #include "camel-session.h"
71 #define DBUS_PATH "/org/gnome/KrbAuthDialog"
72 #define DBUS_INTERFACE "org.gnome.KrbAuthDialog"
73 #define DBUS_METHOD "org.gnome.KrbAuthDialog.acquireTgt"
75 #define CAMEL_SASL_GSSAPI_GET_PRIVATE(obj) \
76 (G_TYPE_INSTANCE_GET_PRIVATE \
77 ((obj), CAMEL_TYPE_SASL_GSSAPI, CamelSaslGssapiPrivate))
79 CamelServiceAuthType camel_sasl_gssapi_authtype = {
82 N_("This option will connect to the server using "
83 "Kerberos 5 authentication."),
91 GSSAPI_STATE_CONTINUE_NEEDED,
92 GSSAPI_STATE_COMPLETE,
93 GSSAPI_STATE_AUTHENTICATED
96 #define GSSAPI_SECURITY_LAYER_NONE (1 << 0)
97 #define GSSAPI_SECURITY_LAYER_INTEGRITY (1 << 1)
98 #define GSSAPI_SECURITY_LAYER_PRIVACY (1 << 2)
100 #define DESIRED_SECURITY_LAYER GSSAPI_SECURITY_LAYER_NONE
102 struct _CamelSaslGssapiPrivate {
108 G_DEFINE_TYPE (CamelSaslGssapi, camel_sasl_gssapi, CAMEL_TYPE_SASL)
111 gssapi_set_exception (OM_uint32 major,
119 str = _("The specified mechanism is not supported by the "
120 "provided credential, or is unrecognized by the "
124 str = _("The provided target_name parameter was ill-formed.");
126 case GSS_S_BAD_NAMETYPE:
127 str = _("The provided target_name parameter contained an "
128 "invalid or unsupported type of name.");
130 case GSS_S_BAD_BINDINGS:
131 str = _("The input_token contains different channel "
132 "bindings to those specified via the "
133 "input_chan_bindings parameter.");
136 str = _("The input_token contains an invalid signature, or a "
137 "signature that could not be verified.");
140 str = _("The supplied credentials were not valid for context "
141 "initiation, or the credential handle did not "
142 "reference any credentials.");
144 case GSS_S_NO_CONTEXT:
145 str = _("The supplied context handle did not refer to a valid context.");
147 case GSS_S_DEFECTIVE_TOKEN:
148 str = _("The consistency checks performed on the input_token failed.");
150 case GSS_S_DEFECTIVE_CREDENTIAL:
151 str = _("The consistency checks performed on the credential failed.");
153 case GSS_S_CREDENTIALS_EXPIRED:
154 str = _("The referenced credentials have expired.");
157 str = error_message (minor);
160 str = _("Bad authentication response from server.");
164 error, CAMEL_SERVICE_ERROR,
165 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
170 sasl_gssapi_finalize (GObject *object)
172 CamelSaslGssapi *sasl = CAMEL_SASL_GSSAPI (object);
175 if (sasl->priv->ctx != GSS_C_NO_CONTEXT)
176 gss_delete_sec_context (
177 &status, &sasl->priv->ctx, GSS_C_NO_BUFFER);
179 if (sasl->priv->target != GSS_C_NO_NAME)
180 gss_release_name (&status, &sasl->priv->target);
182 /* Chain up to parent's finalize() method. */
183 G_OBJECT_CLASS (camel_sasl_gssapi_parent_class)->finalize (object);
186 /* DBUS Specific code */
189 send_dbus_message (gchar *name)
191 gint success = FALSE;
192 GError *error = NULL;
193 GDBusConnection *connection;
194 GDBusMessage *message, *reply;
196 connection = g_bus_get_sync (G_BUS_TYPE_SESSION, NULL, &error);
198 g_warning ("could not get system bus: %s\n", error->message);
199 g_error_free (error);
204 g_dbus_connection_set_exit_on_close (connection, FALSE);
206 /* Create a new message on the DBUS_INTERFACE */
207 message = g_dbus_message_new_method_call (DBUS_INTERFACE, DBUS_PATH, DBUS_INTERFACE, "acquireTgt");
209 g_object_unref (connection);
213 /* Appends the data as an argument to the message */
214 if (strchr (name, '\\'))
215 name = strchr (name, '\\');
216 g_dbus_message_set_body (message, g_variant_new ("(s)", name));
218 /* Sends the message: Have a 300 sec wait timeout */
219 reply = g_dbus_connection_send_message_with_reply_sync (connection, message, G_DBUS_SEND_MESSAGE_FLAGS_NONE, 300 * 1000, NULL, NULL, &error);
222 g_warning ("%s: %s\n", G_STRFUNC, error->message);
223 g_error_free (error);
227 GVariant *body = g_dbus_message_get_body (reply);
229 success = body && g_variant_get_boolean (body);
231 g_object_unref (reply);
234 /* Free the message */
235 g_object_unref (message);
236 g_object_unref (connection);
244 sasl_gssapi_challenge (CamelSasl *sasl,
248 CamelSaslGssapiPrivate *priv;
249 CamelService *service;
250 OM_uint32 major, minor, flags, time;
251 gss_buffer_desc inbuf, outbuf;
252 GByteArray *challenge = NULL;
253 gss_buffer_t input_token;
258 struct addrinfo *ai, hints;
259 const gchar *service_name;
261 priv = CAMEL_SASL_GSSAPI_GET_PRIVATE (sasl);
263 service = camel_sasl_get_service (sasl);
264 service_name = camel_sasl_get_service_name (sasl);
266 switch (priv->state) {
267 case GSSAPI_STATE_INIT:
268 memset (&hints, 0, sizeof (hints));
269 hints.ai_flags = AI_CANONNAME;
270 ai = camel_getaddrinfo(service->url->host?service->url->host:"localhost", NULL, &hints, error);
274 str = g_strdup_printf("%s@%s", service_name, ai->ai_canonname);
275 camel_freeaddrinfo (ai);
278 inbuf.length = strlen (str);
279 major = gss_import_name (&minor, &inbuf, GSS_C_NT_HOSTBASED_SERVICE, &priv->target);
282 if (major != GSS_S_COMPLETE) {
283 gssapi_set_exception (major, minor, error);
287 input_token = GSS_C_NO_BUFFER;
291 case GSSAPI_STATE_CONTINUE_NEEDED:
294 error, CAMEL_SERVICE_ERROR,
295 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
296 _("Bad authentication response from server."));
300 inbuf.value = token->data;
301 inbuf.length = token->len;
302 input_token = &inbuf;
305 major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target,
306 GSS_C_OID_KRBV5_DES, GSS_C_MUTUAL_FLAG |
307 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
308 0, GSS_C_NO_CHANNEL_BINDINGS,
309 input_token, &mech, &outbuf, &flags, &time);
313 priv->state = GSSAPI_STATE_COMPLETE;
315 case GSS_S_CONTINUE_NEEDED:
316 priv->state = GSSAPI_STATE_CONTINUE_NEEDED;
319 if (major == (OM_uint32)GSS_S_FAILURE &&
320 (minor == (OM_uint32)KRB5KRB_AP_ERR_TKT_EXPIRED ||
321 minor == (OM_uint32)KRB5KDC_ERR_NEVER_VALID) &&
322 send_dbus_message (service->url->user))
325 gssapi_set_exception (major, minor, error);
329 challenge = g_byte_array_new ();
330 g_byte_array_append (challenge, outbuf.value, outbuf.length);
331 #ifndef HAVE_HEIMDAL_KRB5
332 gss_release_buffer (&minor, &outbuf);
335 case GSSAPI_STATE_COMPLETE:
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;
347 major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop);
348 if (major != GSS_S_COMPLETE) {
349 gssapi_set_exception (major, minor, error);
353 if (outbuf.length < 4) {
355 error, CAMEL_SERVICE_ERROR,
356 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
357 _("Bad authentication response from server."));
358 #ifndef HAVE_HEIMDAL_KRB5
359 gss_release_buffer (&minor, &outbuf);
364 /* check that our desired security layer is supported */
365 if ((((guchar *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
367 error, CAMEL_SERVICE_ERROR,
368 CAMEL_SERVICE_ERROR_CANT_AUTHENTICATE,
369 _("Unsupported security layer."));
370 #ifndef HAVE_HEIMDAL_KRB5
371 gss_release_buffer (&minor, &outbuf);
376 inbuf.length = 4 + strlen (service->url->user);
377 inbuf.value = str = g_malloc (inbuf.length);
378 memcpy (inbuf.value, outbuf.value, 4);
379 str[0] = DESIRED_SECURITY_LAYER;
380 memcpy (str + 4, service->url->user, inbuf.length - 4);
382 #ifndef HAVE_HEIMDAL_KRB5
383 gss_release_buffer (&minor, &outbuf);
386 major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
387 if (major != GSS_S_COMPLETE) {
388 gssapi_set_exception (major, minor, error);
394 challenge = g_byte_array_new ();
395 g_byte_array_append (challenge, outbuf.value, outbuf.length);
397 #ifndef HAVE_HEIMDAL_KRB5
398 gss_release_buffer (&minor, &outbuf);
401 priv->state = GSSAPI_STATE_AUTHENTICATED;
403 camel_sasl_set_authenticated (sasl, TRUE);
413 camel_sasl_gssapi_class_init (CamelSaslGssapiClass *class)
415 GObjectClass *object_class;
416 CamelSaslClass *sasl_class;
418 g_type_class_add_private (class, sizeof (CamelSaslGssapiPrivate));
420 object_class = G_OBJECT_CLASS (class);
421 object_class->finalize = sasl_gssapi_finalize;
423 sasl_class = CAMEL_SASL_CLASS (class);
424 sasl_class->challenge = sasl_gssapi_challenge;
428 camel_sasl_gssapi_init (CamelSaslGssapi *sasl)
430 sasl->priv = CAMEL_SASL_GSSAPI_GET_PRIVATE (sasl);
432 sasl->priv->state = GSSAPI_STATE_INIT;
433 sasl->priv->ctx = GSS_C_NO_CONTEXT;
434 sasl->priv->target = GSS_C_NO_NAME;
437 #endif /* HAVE_KRB5 */