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>
66 #include <dbus/dbus.h>
67 #include <dbus/dbus-glib-lowlevel.h>
69 #include "camel-net-utils.h"
70 #include "camel-sasl-gssapi.h"
71 #include "camel-session.h"
73 #define DBUS_PATH "/org/gnome/KrbAuthDialog"
74 #define DBUS_INTERFACE "org.gnome.KrbAuthDialog"
75 #define DBUS_METHOD "org.gnome.KrbAuthDialog.acquireTgt"
77 CamelServiceAuthType camel_sasl_gssapi_authtype = {
80 N_("This option will connect to the server using "
81 "Kerberos 5 authentication."),
89 GSSAPI_STATE_CONTINUE_NEEDED,
90 GSSAPI_STATE_COMPLETE,
91 GSSAPI_STATE_AUTHENTICATED
94 #define GSSAPI_SECURITY_LAYER_NONE (1 << 0)
95 #define GSSAPI_SECURITY_LAYER_INTEGRITY (1 << 1)
96 #define GSSAPI_SECURITY_LAYER_PRIVACY (1 << 2)
98 #define DESIRED_SECURITY_LAYER GSSAPI_SECURITY_LAYER_NONE
100 struct _CamelSaslGssapiPrivate {
106 static CamelSaslClass *parent_class = NULL;
109 gssapi_set_exception (OM_uint32 major,
117 str = _("The specified mechanism is not supported by the "
118 "provided credential, or is unrecognized by the "
122 str = _("The provided target_name parameter was ill-formed.");
124 case GSS_S_BAD_NAMETYPE:
125 str = _("The provided target_name parameter contained an "
126 "invalid or unsupported type of name.");
128 case GSS_S_BAD_BINDINGS:
129 str = _("The input_token contains different channel "
130 "bindings to those specified via the "
131 "input_chan_bindings parameter.");
134 str = _("The input_token contains an invalid signature, or a "
135 "signature that could not be verified.");
138 str = _("The supplied credentials were not valid for context "
139 "initiation, or the credential handle did not "
140 "reference any credentials.");
142 case GSS_S_NO_CONTEXT:
143 str = _("The supplied context handle did not refer to a valid context.");
145 case GSS_S_DEFECTIVE_TOKEN:
146 str = _("The consistency checks performed on the input_token failed.");
148 case GSS_S_DEFECTIVE_CREDENTIAL:
149 str = _("The consistency checks performed on the credential failed.");
151 case GSS_S_CREDENTIALS_EXPIRED:
152 str = _("The referenced credentials have expired.");
155 str = error_message (minor);
158 str = _("Bad authentication response from server.");
161 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, str);
165 sasl_gssapi_finalize (CamelObject *object)
167 CamelSaslGssapi *sasl = CAMEL_SASL_GSSAPI (object);
170 if (sasl->priv->ctx != GSS_C_NO_CONTEXT)
171 gss_delete_sec_context (
172 &status, &sasl->priv->ctx, GSS_C_NO_BUFFER);
174 if (sasl->priv->target != GSS_C_NO_NAME)
175 gss_release_name (&status, &sasl->priv->target);
180 /* DBUS Specific code */
183 send_dbus_message (gchar *name)
185 DBusMessage *message, *reply;
186 DBusError dbus_error;
187 gint success = FALSE;
188 DBusConnection *bus = NULL;
190 dbus_error_init (&dbus_error);
191 if (!(bus = dbus_bus_get (DBUS_BUS_SESSION, &dbus_error))) {
192 g_warning ("could not get system bus: %s\n", dbus_error.message);
193 dbus_error_free (&dbus_error);
197 dbus_error_free (&dbus_error);
199 dbus_connection_setup_with_g_main (bus, NULL);
200 dbus_connection_set_exit_on_disconnect (bus, FALSE);
202 /* Create a new message on the DBUS_INTERFACE */
203 if (!(message = dbus_message_new_method_call (DBUS_INTERFACE, DBUS_PATH, DBUS_INTERFACE, "acquireTgt"))) {
204 g_object_unref (bus);
207 /* Appends the data as an argument to the message */
208 if (strchr(name, '\\'))
209 name = strchr(name, '\\');
210 dbus_message_append_args (message,
211 DBUS_TYPE_STRING, &name,
213 dbus_error_init(&dbus_error);
215 /* Sends the message: Have a 300 sec wait timeout */
216 reply = dbus_connection_send_with_reply_and_block (bus, message, 300 * 1000, &dbus_error);
218 if (dbus_error_is_set(&dbus_error))
219 g_warning ("%s: %s\n", dbus_error.name, dbus_error.message);
220 dbus_error_free(&dbus_error);
224 dbus_error_init(&dbus_error);
225 dbus_message_get_args(reply, &dbus_error, DBUS_TYPE_BOOLEAN, &success, DBUS_TYPE_INVALID);
226 dbus_error_free(&dbus_error);
227 dbus_message_unref(reply);
230 /* Free the message */
231 dbus_message_unref (message);
232 dbus_connection_unref (bus);
240 sasl_gssapi_challenge (CamelSasl *sasl,
244 CamelSaslGssapiPrivate *priv = CAMEL_SASL_GSSAPI (sasl)->priv;
245 CamelService *service;
246 OM_uint32 major, minor, flags, time;
247 gss_buffer_desc inbuf, outbuf;
248 GByteArray *challenge = NULL;
249 gss_buffer_t input_token;
254 struct addrinfo *ai, hints;
255 const gchar *service_name;
257 service = camel_sasl_get_service (sasl);
258 service_name = camel_sasl_get_service_name (sasl);
260 switch (priv->state) {
261 case GSSAPI_STATE_INIT:
262 memset(&hints, 0, sizeof(hints));
263 hints.ai_flags = AI_CANONNAME;
264 ai = camel_getaddrinfo(service->url->host?service->url->host:"localhost", NULL, &hints, ex);
268 str = g_strdup_printf("%s@%s", service_name, ai->ai_canonname);
269 camel_freeaddrinfo(ai);
272 inbuf.length = strlen (str);
273 major = gss_import_name (&minor, &inbuf, GSS_C_NT_HOSTBASED_SERVICE, &priv->target);
276 if (major != GSS_S_COMPLETE) {
277 gssapi_set_exception (major, minor, ex);
281 input_token = GSS_C_NO_BUFFER;
285 case GSSAPI_STATE_CONTINUE_NEEDED:
287 camel_exception_set (
288 ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
289 _("Bad authentication response from server."));
293 inbuf.value = token->data;
294 inbuf.length = token->len;
295 input_token = &inbuf;
298 major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target,
299 GSS_C_OID_KRBV5_DES, GSS_C_MUTUAL_FLAG |
300 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
301 0, GSS_C_NO_CHANNEL_BINDINGS,
302 input_token, &mech, &outbuf, &flags, &time);
306 priv->state = GSSAPI_STATE_COMPLETE;
308 case GSS_S_CONTINUE_NEEDED:
309 priv->state = GSSAPI_STATE_CONTINUE_NEEDED;
312 if (major == (OM_uint32)GSS_S_FAILURE &&
313 (minor == (OM_uint32)KRB5KRB_AP_ERR_TKT_EXPIRED ||
314 minor == (OM_uint32)KRB5KDC_ERR_NEVER_VALID)) {
315 if (send_dbus_message (service->url->user))
318 gssapi_set_exception (major, minor, ex);
322 challenge = g_byte_array_new ();
323 g_byte_array_append (challenge, outbuf.value, outbuf.length);
324 #ifndef HAVE_HEIMDAL_KRB5
325 gss_release_buffer (&minor, &outbuf);
328 case GSSAPI_STATE_COMPLETE:
330 camel_exception_set (
331 ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
332 _("Bad authentication response from server."));
336 inbuf.value = token->data;
337 inbuf.length = token->len;
339 major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop);
340 if (major != GSS_S_COMPLETE) {
341 gssapi_set_exception (major, minor, ex);
345 if (outbuf.length < 4) {
346 camel_exception_set (
347 ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
348 _("Bad authentication response from server."));
349 #ifndef HAVE_HEIMDAL_KRB5
350 gss_release_buffer (&minor, &outbuf);
355 /* check that our desired security layer is supported */
356 if ((((guchar *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
357 camel_exception_set (
358 ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
359 _("Unsupported security layer."));
360 #ifndef HAVE_HEIMDAL_KRB5
361 gss_release_buffer (&minor, &outbuf);
366 inbuf.length = 4 + strlen (service->url->user);
367 inbuf.value = str = g_malloc (inbuf.length);
368 memcpy (inbuf.value, outbuf.value, 4);
369 str[0] = DESIRED_SECURITY_LAYER;
370 memcpy (str + 4, service->url->user, inbuf.length - 4);
372 #ifndef HAVE_HEIMDAL_KRB5
373 gss_release_buffer (&minor, &outbuf);
376 major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
377 if (major != GSS_S_COMPLETE) {
378 gssapi_set_exception (major, minor, ex);
384 challenge = g_byte_array_new ();
385 g_byte_array_append (challenge, outbuf.value, outbuf.length);
387 #ifndef HAVE_HEIMDAL_KRB5
388 gss_release_buffer (&minor, &outbuf);
391 priv->state = GSSAPI_STATE_AUTHENTICATED;
393 camel_sasl_set_authenticated (sasl, TRUE);
403 camel_sasl_gssapi_class_init (CamelSaslGssapiClass *class)
405 CamelSaslClass *sasl_class;
407 parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ()));
409 sasl_class = CAMEL_SASL_CLASS (class);
410 sasl_class->challenge = sasl_gssapi_challenge;
414 camel_sasl_gssapi_init (CamelSaslGssapi *sasl)
416 sasl->priv = g_new (struct _CamelSaslGssapiPrivate, 1);
418 sasl->priv->state = GSSAPI_STATE_INIT;
419 sasl->priv->ctx = GSS_C_NO_CONTEXT;
420 sasl->priv->target = GSS_C_NO_NAME;
424 camel_sasl_gssapi_get_type (void)
426 static CamelType type = CAMEL_INVALID_TYPE;
428 if (type == CAMEL_INVALID_TYPE) {
429 type = camel_type_register (
430 camel_sasl_get_type (),
432 sizeof (CamelSaslGssapi),
433 sizeof (CamelSaslGssapiClass),
434 (CamelObjectClassInitFunc) camel_sasl_gssapi_class_init,
436 (CamelObjectInitFunc) camel_sasl_gssapi_init,
437 (CamelObjectFinalizeFunc) sasl_gssapi_finalize);
443 #endif /* HAVE_KRB5 */