aboutsummaryrefslogblamecommitdiffstats
path: root/camel/camel-sasl-gssapi.c
blob: 4f15c438214bee6f66eb515ce071a097f47557bd (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 <com_err.h>
#ifdef HAVE_MIT_KRB5
#include <gssapi/gssapi.h>
#include <gssapi/gssapi_generic.h>
#else /* HAVE_HEIMDAL_KRB5 */
#include <gssapi.h>
#define gss_nt_service_name GSS_C_NT_HOSTBASED_SERVICE
#endif
#include <errno.h>

#ifndef GSS_C_OID_KRBV5_DES
#define GSS_C_OID_KRBV5_DES GSS_C_NO_OID
#endif

#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
};

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 GByteArray *gssapi_challenge (CamelSasl *sasl, GByteArray *token, CamelException *ex);


static CamelSaslClass *parent_class = NULL;


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 *gssapi = CAMEL_SASL_GSSAPI (object);
    guint32 status;
    
    if (gssapi->priv->ctx != GSS_C_NO_CONTEXT)
        gss_delete_sec_context (&status, &gssapi->priv->ctx, GSS_C_NO_BUFFER);
    
    if (gssapi->priv->target != GSS_C_NO_NAME)
        gss_release_name (&status, &gssapi->priv->target);
    
    g_free (gssapi->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 = _("The supplied context handle did not refer to a valid context.");
        break;
    case GSS_S_DEFECTIVE_TOKEN:
        str = _("The 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:
        str = error_message (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))) {
            camel_exception_setv (ex, CAMEL_EXCEPTION_SYSTEM,
                          _("Failed to resolve host `%s': %s"),
                          sasl->service->url->host, g_strerror (errno));
            return NULL;
        }
        
        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, GSS_C_MUTUAL_FLAG |
                          GSS_C_REPLAY_FLAG | GSS_C_SEQUENCE_FLAG,
                          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);
            return NULL;
        }
        
        challenge = g_byte_array_new ();
        g_byte_array_append (challenge, outbuf.value, outbuf.length);
#ifndef HAVE_HEIMDAL_KRB5
        gss_release_buffer (&minor, &outbuf);
#endif
        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);
            return NULL;
        }
        
        if (outbuf.length < 4) {
            camel_exception_set (ex, CAMEL_EXCEPTION_SERVICE_CANT_AUTHENTICATE,
                         _("Bad authentication response from server."));
#ifndef HAVE_HEIMDAL_KRB5
            gss_release_buffer (&minor, &outbuf);
#endif
            return NULL;
        }
        
        /* check that our desired security layer is supported */
        if ((((unsigned char *) outbuf.value)[0] & DESIRED_SECURITY_LAYER) != 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);
        
#ifndef HAVE_HEIMDAL_KRB5
        gss_release_buffer (&minor, &outbuf);
#endif
        
        major = gss_wrap (&minor, priv->ctx, FALSE, qop, &inbuf, &conf_state, &outbuf);
        if (major != GSS_S_COMPLETE) {
            gssapi_set_exception (major, minor, ex);
            g_free (str);
            return NULL;
        }
        
        g_free (str);
        challenge = g_byte_array_new ();
        g_byte_array_append (challenge, outbuf.value, outbuf.length);
        
#ifndef HAVE_HEIMDAL_KRB5
        gss_release_buffer (&minor, &outbuf);
#endif
        
        priv->state = GSSAPI_STATE_AUTHENTICATED;
        
        sasl->authenticated = TRUE;
        break;
    default:
        return NULL;
    }
    
    return challenge;
}

#endif /* HAVE_KRB5 */