aboutsummaryrefslogblamecommitdiffstats
path: root/camel/camel-sasl-gssapi.c
blob: 938e038ed3b9efead38de3d18f259ee746e7ea31 (plain) (tree)





















































































































































































































































































































                                                                                                    
/* -*- 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 */