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-lib.h>
60 #include "camel-net-utils.h"
61 #include "camel-sasl-gssapi.h"
63 CamelServiceAuthType camel_sasl_gssapi_authtype = {
66 N_("This option will connect to the server using "
67 "Kerberos 5 authentication."),
75 GSSAPI_STATE_CONTINUE_NEEDED,
76 GSSAPI_STATE_COMPLETE,
77 GSSAPI_STATE_AUTHENTICATED
80 #define GSSAPI_SECURITY_LAYER_NONE (1 << 0)
81 #define GSSAPI_SECURITY_LAYER_INTEGRITY (1 << 1)
82 #define GSSAPI_SECURITY_LAYER_PRIVACY (1 << 2)
84 #define DESIRED_SECURITY_LAYER GSSAPI_SECURITY_LAYER_NONE
86 struct _CamelSaslGssapiPrivate {
93 static GByteArray *gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex);
96 static CamelSaslClass *parent_class = NULL;
100 camel_sasl_gssapi_class_init (CamelSaslGssapiClass *klass)
102 CamelSaslClass *camel_sasl_class = CAMEL_SASL_CLASS (klass);
104 parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ()));
106 /* virtual method overload */
107 camel_sasl_class->challenge = gssapi_challenge;
111 camel_sasl_gssapi_init (gpointer object, gpointer klass)
113 CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
115 gssapi->priv = g_new (struct _CamelSaslGssapiPrivate, 1);
116 gssapi->priv->state = GSSAPI_STATE_INIT;
117 gssapi->priv->ctx = GSS_C_NO_CONTEXT;
118 gssapi->priv->target = GSS_C_NO_NAME;
122 camel_sasl_gssapi_finalize (CamelObject *object)
124 CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object);
127 if (gssapi->priv->ctx != GSS_C_NO_CONTEXT)
128 gss_delete_sec_context (&status, &gssapi->priv->ctx, GSS_C_NO_BUFFER);
130 if (gssapi->priv->target != GSS_C_NO_NAME)
131 gss_release_name (&status, &gssapi->priv->target);
133 g_free (gssapi->priv);
138 camel_sasl_gssapi_get_type (void)
140 static CamelType type = CAMEL_INVALID_TYPE;
142 if (type == CAMEL_INVALID_TYPE) {
143 type = camel_type_register (
144 camel_sasl_get_type (),
146 sizeof (CamelSaslGssapi),
147 sizeof (CamelSaslGssapiClass),
148 (CamelObjectClassInitFunc) camel_sasl_gssapi_class_init,
150 (CamelObjectInitFunc) camel_sasl_gssapi_init,
151 (CamelObjectFinalizeFunc) camel_sasl_gssapi_finalize);
158 gssapi_set_exception (OM_uint32 major, OM_uint32 minor, CamelException *ex)
164 str = _("The specified mechanism is not supported by the "
165 "provided credential, or is unrecognized by the "
169 str = _("The provided target_name parameter was ill-formed.");
171 case GSS_S_BAD_NAMETYPE:
172 str = _("The provided target_name parameter contained an "
173 "invalid or unsupported type of name.");
175 case GSS_S_BAD_BINDINGS:
176 str = _("The input_token contains different channel "
177 "bindings to those specified via the "
178 "input_chan_bindings parameter.");
181 str = _("The input_token contains an invalid signature, or a "
182 "signature that could not be verified.");
185 str = _("The supplied credentials were not valid for context "
186 "initiation, or the credential handle did not "
187 "reference any credentials.");
189 case GSS_S_NO_CONTEXT:
190 str = _("The supplied context handle did not refer to a valid context.");
192 case GSS_S_DEFECTIVE_TOKEN:
193 str = _("The consistency checks performed on the input_token failed.");
195 case GSS_S_DEFECTIVE_CREDENTIAL:
196 str = _("The consistency checks performed on the credential failed.");
198 case GSS_S_CREDENTIALS_EXPIRED:
199 str = _("The referenced credentials have expired.");
202 str = error_message (minor);
205 str = _("Bad authentication response from server.");
208 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, str);
212 gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex)
214 struct _CamelSaslGssapiPrivate *priv = CAMEL_SASL_GSSAPI (sasl)->priv;
215 OM_uint32 major, minor, flags, time;
216 gss_buffer_desc inbuf, outbuf;
217 GByteArray *challenge = NULL;
218 gss_buffer_t input_token;
223 struct addrinfo *ai, hints;
225 switch (priv->state) {
226 case GSSAPI_STATE_INIT:
227 memset(&hints, 0, sizeof(hints));
228 hints.ai_flags = AI_CANONNAME;
229 ai = camel_getaddrinfo(sasl->service->url->host?sasl->service->url->host:"localhost", NULL, &hints, ex);
233 str = g_strdup_printf("%s@%s", sasl->service_name, ai->ai_canonname);
234 camel_freeaddrinfo(ai);
237 inbuf.length = strlen (str);
238 major = gss_import_name (&minor, &inbuf, GSS_C_NT_HOSTBASED_SERVICE, &priv->target);
241 if (major != GSS_S_COMPLETE) {
242 gssapi_set_exception (major, minor, ex);
246 input_token = GSS_C_NO_BUFFER;
250 case GSSAPI_STATE_CONTINUE_NEEDED:
252 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
253 _("Bad authentication response from server."));
257 inbuf.value = token->data;
258 inbuf.length = token->len;
259 input_token = &inbuf;
262 major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target,
263 GSS_C_OID_KRBV5_DES, GSS_C_MUTUAL_FLAG |
264 GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
265 0, GSS_C_NO_CHANNEL_BINDINGS,
266 input_token, &mech, &outbuf, &flags, &time);
270 priv->state = GSSAPI_STATE_COMPLETE;
272 case GSS_S_CONTINUE_NEEDED:
273 priv->state = GSSAPI_STATE_CONTINUE_NEEDED;
276 gssapi_set_exception (major, minor, ex);
280 challenge = g_byte_array_new ();
281 g_byte_array_append (challenge, outbuf.value, outbuf.length);
282 #ifndef HAVE_HEIMDAL_KRB5
283 gss_release_buffer (&minor, &outbuf);
286 case GSSAPI_STATE_COMPLETE:
288 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
289 _("Bad authentication response from server."));
293 inbuf.value = token->data;
294 inbuf.length = token->len;
296 major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop);
297 if (major != GSS_S_COMPLETE) {
298 gssapi_set_exception (major, minor, ex);
302 if (outbuf.length < 4) {
303 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
304 _("Bad authentication response from server."));
305 #ifndef HAVE_HEIMDAL_KRB5
306 gss_release_buffer (&minor, &outbuf);
311 /* check that our desired security layer is supported */
312 if ((((unsigned char *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != DESIRED_SECURITY_LAYER) {
313 camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
314 _("Unsupported security layer."));
315 #ifndef HAVE_HEIMDAL_KRB5
316 gss_release_buffer (&minor, &outbuf);
321 inbuf.length = 4 + strlen (sasl->service->url->user);
322 inbuf.value = str = g_malloc (inbuf.length);
323 memcpy (inbuf.value, outbuf.value, 4);
324 str[0] = DESIRED_SECURITY_LAYER;
325 memcpy (str + 4, sasl->service->url->user, inbuf.length - 4);
327 #ifndef HAVE_HEIMDAL_KRB5
328 gss_release_buffer (&minor, &outbuf);
331 major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
332 if (major != GSS_S_COMPLETE) {
333 gssapi_set_exception (major, minor, ex);
339 challenge = g_byte_array_new ();
340 g_byte_array_append (challenge, outbuf.value, outbuf.length);
342 #ifndef HAVE_HEIMDAL_KRB5
343 gss_release_buffer (&minor, &outbuf);
346 priv->state = GSSAPI_STATE_AUTHENTICATED;
348 sasl->authenticated = TRUE;
357 #endif /* HAVE_KRB5 */