diff options
Diffstat (limited to 'camel/camel-sasl-gssapi.c')
-rw-r--r-- | camel/camel-sasl-gssapi.c | 310 |
1 files changed, 310 insertions, 0 deletions
diff --git a/camel/camel-sasl-gssapi.c b/camel/camel-sasl-gssapi.c new file mode 100644 index 0000000000..938e038ed3 --- /dev/null +++ b/camel/camel-sasl-gssapi.c @@ -0,0 +1,310 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * Authors: Jeffrey Stedfast <fejj@ximian.com> + * + * Copyright 2003 Ximian, Inc. (www.ximian.com) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Street #330, Boston, MA 02111-1307, USA. + * + */ + + +#ifdef HAVE_CONFIG_H +#include <config.h> +#endif + +#ifdef HAVE_KRB5 + +#include <string.h> +#include <gssapi/gssapi.h> + +#include "camel-sasl-gssapi.h" + +CamelServiceAuthType camel_sasl_gssapi_authtype = { + N_("GSSAPI"), + + N_("This option will connect to the server using " + "Kerberos 5 authentication."), + + "GSSAPI", + FALSE +}; + +static CamelSaslClass *parent_class = NULL; + +enum { + GSSAPI_STATE_INIT, + GSSAPI_STATE_CONTINUE_NEEDED, + GSSAPI_STATE_COMPLETE, + GSSAPI_STATE_AUTHENTICATED +}; + +#define GSSAPI_SECURITY_LAYER_NONE (1 << 0) +#define GSSAPI_SECURITY_LAYER_INTEGRITY (1 << 1) +#define GSSAPI_SECURITY_LAYER_PRIVACY (1 << 2) + +#define DESIRED_SECURITY_LAYER GSSAPI_SECURITY_LAYER_NONE + +struct _CamelSaslGssapiPrivate { + int state; + gss_ctx_id_t ctx; + gss_name_t target; +}; + +static void +camel_sasl_gssapi_class_init (CamelSaslGssapiClass *klass) +{ + CamelSaslClass *camel_sasl_class = CAMEL_SASL_CLASS (klass); + + parent_class = CAMEL_SASL_CLASS (camel_type_get_global_classfuncs (camel_sasl_get_type ())); + + /* virtual method overload */ + camel_sasl_class->challenge = gssapi_challenge; +} + +static void +camel_sasl_gssapi_init (gpointer object, gpointer klass) +{ + CamelSaslGssapi *gssapi = CAMEL_SASL_GSSAPI (object); + + gssapi->priv = g_new (struct _CamelSaslGssapiPrivate, 1); + gssapi->priv->state = GSSAPI_STATE_INIT; + gssapi->priv->ctx = GSS_C_NO_CONTEXT; + gssapi->priv->target = GSS_C_NO_NAME; +} + +static void +camel_sasl_gssapi_finalize (CamelObject *object) +{ + CamelSaslGssapi *sasl = CAMEL_SASL_GSSAPI (object); + guint32 status; + + if (sasl->priv) { + gss_delete_sec_context (&status, sasl->priv->ctx); + gss_release_name (&status, sasl->priv->target); + g_free (sasl->priv); + } +} + + +CamelType +camel_sasl_gssapi_get_type (void) +{ + static CamelType type = CAMEL_INVALID_TYPE; + + if (type == CAMEL_INVALID_TYPE) { + type = camel_type_register ( + camel_sasl_get_type (), + "CamelSaslGssapi", + sizeof (CamelSaslGssapi), + sizeof (CamelSaslGssapiClass), + (CamelObjectClassInitFunc) camel_sasl_gssapi_class_init, + NULL, + (CamelObjectInitFunc) camel_sasl_gssapi_init, + (CamelObjectFinalizeFunc) camel_sasl_gssapi_finalize); + } + + return type; +} + +static void +gssapi_set_exception (OM_uint32 major, OM_uint32 minor, CamelException *ex) +{ + const char *str; + + switch (major) { + case GSS_S_BAD_MECH: + str = _("The specified mechanism is not supported by the " + "provided credential, or is unrecognized by the " + "implementation."); + break; + case GSS_S_BAD_NAME: + str = _("The provided target_name parameter was ill-formed."); + break; + case GSS_S_BAD_NAMETYPE: + str = _("The provided target_name parameter contained an " + "invalid or unsupported type of name."); + break; + case GSS_S_BAD_BINDINGS: + str = _("The input_token contains different channel " + "bindings to those specified via the " + "input_chan_bindings parameter."); + break; + case GSS_S_BAD_SIG: + str = _("The input_token contains an invalid signature, or a " + "signature that could not be verified."); + break; + case GSS_S_NO_CRED: + str = _("The supplied credentials were not valid for context " + "initiation, or the credential handle did not " + "reference any credentials."); + break; + case GSS_S_NO_CONTEXT: + str = _("Indicates that the supplied context handle did not " + "refer to a valid context."); + break; + case GSS_S_DEFECTIVE_TOKEN: + str = _("Indicates that consistency checks performed on " + "the input_token failed."); + break; + case GSS_S_DEFECTIVE_CREDENTIAL: + str = _("The consistency checks performed on the credential failed."); + break; + case GSS_S_CREDENTIALS_EXPIRED: + str = _("The referenced credentials have expired."); + break; + case GSS_S_FAILURE: + switch (minor) { + } + break; + default: + str = _("Bad authentication response from server."); + } + + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, str); +} + +static GByteArray * +gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex) +{ + struct _CamelSaslGssapiPrivate *priv = CAMEL_SASL_GSSAPI (sasl)->priv; + OM_uint32 major, minor, flags, time; + gss_buffer_desc inbuf, outbuf; + GByteArray *challenge = NULL; + gss_buffer_t input_token; + struct hostent *h; + int conf_state; + gss_qop_t qop; + gss_OID mech; + char *str; + + switch (priv->state) { + case GSSAPI_STATE_INIT: + if (!(h = camel_service_gethost (sasl->service, ex))) + goto lose; + + str = g_strdup_printf ("%s@%s", sasl->service_name, h->h_name); + camel_free_host (h); + + inbuf.value = str; + inbuf.length = strlen (str); + major = gss_import_name (&minor, &inbuf, gss_nt_service_name, &priv->target); + g_free (str); + + if (major != GSS_S_COMPLETE) { + gssapi_set_exception (major, minor, ex); + return NULL; + } + + input_token = GSS_C_NO_BUFFER; + + goto challenge; + break; + case GSSAPI_STATE_CONTINUE_NEEDED: + if (token == NULL) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Bad authentication response from server.")); + return NULL; + } + + inbuf.value = token->data; + inbuf.length = token->len; + input_token = &inbuf; + + challenge: + major = gss_init_sec_context (&minor, GSS_C_NO_CREDENTIAL, &priv->ctx, priv->target, + GSS_C_OID_KRBV5_DES, 0, 0, GSS_C_NO_CHANNEL_BINDINGS, + input_token, &mech, &outbuf, &flags, &time); + + switch (major) { + case GSS_S_COMPLETE: + priv->state = GSSAPI_STATE_COMPLETE; + break; + case GSS_S_CONTINUE_NEEDED: + priv->state = GSSAPI_STATE_CONTINUE_NEEDED; + break; + default: + gssapi_set_exception (major, minor, ex); + gss_release_buffer (&minor, &outbuf); + return NULL; + } + + challenge = g_byte_array_new () + g_byte_array_append (challenge, outbuf.value, outbuf.length); + gss_release_buffer (&minor, &outbuf); + break; + case GSSAPI_STATE_COMPLETE: + if (token == NULL) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Bad authentication response from server.")); + return NULL; + } + + inbuf.value = token->data; + inbuf.length = token->len; + + major = gss_unwrap (&minor, priv->ctx, &inbuf, &outbuf, &conf_state, &qop); + if (major != GSS_S_COMPLETE) { + gssapi_set_exception (major, minor, ex); + gss_release_buffer (&minor, &outbuf); + return NULL; + } + + if (outbuf.length < 4) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Bad authentication response from server.")); + gss_release_buffer (&minor, &outbuf); + return NULL; + } + + /* check that our desired security layer is supported */ + if (((unsigned char *) outbuf->value)[0] & DESIRED_SECURITY_LAYER) { + camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE, + _("Unsupported security layer.")); + gss_release_buffer (&minor, &outbuf); + return NULL; + } + + inbuf.length = 4 + strlen (sasl->service->url->user); + inbuf.value = str = g_malloc (inbuf.length); + memcpy (inbuf.value, outbuf.value, 4); + str[0] = DESIRED_SECURITY_LAYER; + memcpy (str + 4, sasl->service->url->user, inbuf.length - 4); + gss_release_buffer (&minor, &outbuf); + + major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf); + if (major != 0) { + gssapi_set_exception (major, minor, ex); + gss_release_buffer (&minor, &outbuf); + g_free (str); + return NULL; + } + + challenge = g_byte_array_new (); + g_byte_array_append (challenge, outbuf.value, outbuf.length); + gss_release_buffer (&minor, &outbuf); + + priv->state = GSSAPI_STATE_AUTHENTICATED; + + sasl->authenticated = TRUE; + break; + default: + return NULL; + } + + return challenge; +} + +#endif /* HAVE_KRB5 */ |