1 /* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
3 * Authors: Jeffrey Stedfast <fejj@ximian.com>
5 * Copyright 2003 Ximian, Inc. (www.ximian.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.
33 #include <sys/socket.h>
34 #include <sys/types.h>
35 #ifdef HAVE_ET_COM_ERR_H
36 #include <et/com_err.h>
41 #include <gssapi/gssapi.h>
42 #include <gssapi/gssapi_generic.h>
44 #ifdef HAVE_HEIMDAL_KRB5
48 #include <gssapi/gssapi.h>
49 #include <gssapi/gssapi_ext.h>
50 extern gss_OID gss_nt_service_name;
54 #ifndef GSS_C_OID_KRBV5_DES
55 #define GSS_C_OID_KRBV5_DES GSS_C_NO_OID
58 #include <glib/gi18n.h>
59 #include <glib/gi18n-lib.h>
61 #include "camel-net-utils.h"
62 #include "camel-sasl-gssapi.h"
64 CamelServiceAuthType camel_sasl_gssapi_authtype = {
67 N_("This option will connect to the server using "
68 "Kerberos 5 authentication."),
76 GSSAPI_STATE_CONTINUE_NEEDED,
77 GSSAPI_STATE_COMPLETE,
78 GSSAPI_STATE_AUTHENTICATED
81 #define GSSAPI_SECURITY_LAYER_NONE (1 << 0)
82 #define GSSAPI_SECURITY_LAYER_INTEGRITY (1 << 1)
83 #define GSSAPI_SECURITY_LAYER_PRIVACY (1 << 2)
85 #define DESIRED_SECURITY_LAYER GSSAPI_SECURITY_LAYER_NONE
87 struct _CamelSaslGssapiPrivate {
94 static GByteArray *gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex);
97 static CamelSaslClass *parent_class = NULL;
101 camel_sasl_gssapi_class_init (CamelSaslGssapiClass *klass)
103 CamelSaslClass *camel_sasl_class = CAMEL_SASL_CLASS (klass);
105 parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ()));
107 /* virtual method overload */
108 camel_sasl_class->challenge = gssapi_challenge;
112 camel_sasl_gssapi_init (gpointer object, gpointer klass)
114 CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
116 gssapi->priv = g_new (struct _CamelSaslGssapiPrivate, 1);
117 gssapi->priv->state = GSSAPI_STATE_INIT;
118 gssapi->priv->ctx = GSS_C_NO_CONTEXT;
119 gssapi->priv->target = GSS_C_NO_NAME;
123 camel_sasl_gssapi_finalize (CamelObject *object)
125 CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
128 if (gssapi->priv->ctx != GSS_C_NO_CONTEXT)
129 gss_delete_sec_context (&status, &gssapi->priv->ctx, GSS_C_NO_BUFFER);
131 if (gssapi->priv->target != GSS_C_NO_NAME)
132 gss_release_name (&status, &gssapi->priv->target);
134 g_free (gssapi->priv);
139 camel_sasl_gssapi_get_type (void)
141 static CamelType type = CAMEL_INVALID_TYPE;
143 if (type == CAMEL_INVALID_TYPE) {
144 type = camel_type_register (
145 camel_sasl_get_type (),
147 sizeof (CamelSaslGssapi),
148 sizeof (CamelSaslGssapiClass),
149 (CamelObjectClassInitFunc) camel_sasl_gssapi_class_init,
151 (CamelObjectInitFunc) camel_sasl_gssapi_init,
152 (CamelObjectFinalizeFunc) camel_sasl_gssapi_finalize);
159 gssapi_set_exception (OM_uint32 major, OM_uint32 minor, CamelException *ex)
165 str = _("The specified mechanism is not supported by the "
166 "provided credential, or is unrecognized by the "
170 str = _("The provided target_name parameter was ill-formed.");
172 case GSS_S_BAD_NAMETYPE:
173 str = _("The provided target_name parameter contained an "
174 "invalid or unsupported type of name.");
176 case GSS_S_BAD_BINDINGS:
177 str = _("The input_token contains different channel "
178 "bindings to those specified via the "
179 "input_chan_bindings parameter.");
182 str = _("The input_token contains an invalid signature, or a "
183 "signature that could not be verified.");
186 str = _("The supplied credentials were not valid for context "
187 "initiation, or the credential handle did not "
188 "reference any credentials.");
190 case GSS_S_NO_CONTEXT:
191 str = _("The supplied context handle did not refer to a valid context.");
193 case GSS_S_DEFECTIVE_TOKEN:
194 str = _("The consistency checks performed on the input_token failed.");
196 case GSS_S_DEFECTIVE_CREDENTIAL:
197 str = _("The consistency checks performed on the credential failed.");
199 case GSS_S_CREDENTIALS_EXPIRED:
200 str = _("The referenced credentials have expired.");
203 str = error_message (minor);
206 str = _("Bad authentication response from server.");
209 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, str);
213 gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex)
215 struct _CamelSaslGssapiPrivate *priv = CAMEL_SASL_GSSAPI (sasl)->priv;
216 OM_uint32 major, minor, flags, time;
217 gss_buffer_desc inbuf, outbuf;
218 GByteArray *challenge = NULL;
219 gss_buffer_t input_token;
224 struct addrinfo *ai, hints;
226 switch (priv->state) {
227 case GSSAPI_STATE_INIT:
228 memset(&hints, 0, sizeof(hints));
229 hints.ai_flags = AI_CANONNAME;
230 ai = camel_getaddrinfo(sasl->service->url->host?sasl->service->url->host:"localhost", NULL, &hints, ex);
234 str = g_strdup_printf("%s@%s", sasl->service_name, ai->ai_canonname);
235 camel_freeaddrinfo(ai);
238 inbuf.length = strlen (str);
239 major = gss_import_name (&minor, &inbuf, GSS_C_NT_HOSTBASED_SERVICE, &priv->target);
242 if (major != GSS_S_COMPLETE) {
243 gssapi_set_exception (major, minor, ex);
247 input_token = GSS_C_NO_BUFFER;
251 case GSSAPI_STATE_CONTINUE_NEEDED:
253 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
254 _("Bad authentication response from server."));
258 inbuf.value = token->data;
259 inbuf.length = token->len;
260 input_token = &inbuf;
263 major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target,
264 GSS_C_OID_KRBV5_DES, GSS_C_MUTUAL_FLAG |
265 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
266 0, GSS_C_NO_CHANNEL_BINDINGS,
267 input_token, &mech, &outbuf, &flags, &time);
271 priv->state = GSSAPI_STATE_COMPLETE;
273 case GSS_S_CONTINUE_NEEDED:
274 priv->state = GSSAPI_STATE_CONTINUE_NEEDED;
277 gssapi_set_exception (major, minor, ex);
281 challenge = g_byte_array_new ();
282 g_byte_array_append (challenge, outbuf.value, outbuf.length);
283 #ifndef HAVE_HEIMDAL_KRB5
284 gss_release_buffer (&minor, &outbuf);
287 case GSSAPI_STATE_COMPLETE:
289 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
290 _("Bad authentication response from server."));
294 inbuf.value = token->data;
295 inbuf.length = token->len;
297 major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop);
298 if (major != GSS_S_COMPLETE) {
299 gssapi_set_exception (major, minor, ex);
303 if (outbuf.length < 4) {
304 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
305 _("Bad authentication response from server."));
306 #ifndef HAVE_HEIMDAL_KRB5
307 gss_release_buffer (&minor, &outbuf);
312 /* check that our desired security layer is supported */
313 if ((((unsigned char *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
314 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
315 _("Unsupported security layer."));
316 #ifndef HAVE_HEIMDAL_KRB5
317 gss_release_buffer (&minor, &outbuf);
322 inbuf.length = 4 + strlen (sasl->service->url->user);
323 inbuf.value = str = g_malloc (inbuf.length);
324 memcpy (inbuf.value, outbuf.value, 4);
325 str[0] = DESIRED_SECURITY_LAYER;
326 memcpy (str + 4, sasl->service->url->user, inbuf.length - 4);
328 #ifndef HAVE_HEIMDAL_KRB5
329 gss_release_buffer (&minor, &outbuf);
332 major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
333 if (major != GSS_S_COMPLETE) {
334 gssapi_set_exception (major, minor, ex);
340 challenge = g_byte_array_new ();
341 g_byte_array_append (challenge, outbuf.value, outbuf.length);
343 #ifndef HAVE_HEIMDAL_KRB5
344 gss_release_buffer (&minor, &outbuf);
347 priv->state = GSSAPI_STATE_AUTHENTICATED;
349 sasl->authenticated = TRUE;
358 #endif /* HAVE_KRB5 */