ldmud-3.3.719/
ldmud-3.3.719/doc/
ldmud-3.3.719/doc/efun.de/
ldmud-3.3.719/doc/efun/
ldmud-3.3.719/doc/man/
ldmud-3.3.719/doc/other/
ldmud-3.3.719/mud/
ldmud-3.3.719/mud/heaven7/
ldmud-3.3.719/mud/lp-245/
ldmud-3.3.719/mud/lp-245/banish/
ldmud-3.3.719/mud/lp-245/doc/
ldmud-3.3.719/mud/lp-245/doc/examples/
ldmud-3.3.719/mud/lp-245/doc/sefun/
ldmud-3.3.719/mud/lp-245/log/
ldmud-3.3.719/mud/lp-245/obj/Go/
ldmud-3.3.719/mud/lp-245/players/lars/
ldmud-3.3.719/mud/lp-245/room/death/
ldmud-3.3.719/mud/lp-245/room/maze1/
ldmud-3.3.719/mud/lp-245/room/sub/
ldmud-3.3.719/mud/lp-245/secure/
ldmud-3.3.719/mud/sticklib/
ldmud-3.3.719/mud/sticklib/src/
ldmud-3.3.719/mudlib/deprecated/
ldmud-3.3.719/mudlib/uni-crasher/
ldmud-3.3.719/pkg/
ldmud-3.3.719/pkg/debugger/
ldmud-3.3.719/pkg/diff/
ldmud-3.3.719/pkg/misc/
ldmud-3.3.719/src/
ldmud-3.3.719/src/autoconf/
ldmud-3.3.719/src/ptmalloc/
ldmud-3.3.719/src/util/
ldmud-3.3.719/src/util/erq/
ldmud-3.3.719/src/util/indent/hosts/next/
ldmud-3.3.719/src/util/xerq/
ldmud-3.3.719/src/util/xerq/lpc/
ldmud-3.3.719/src/util/xerq/lpc/www/
ldmud-3.3.719/test/generic/
ldmud-3.3.719/test/inc/
ldmud-3.3.719/test/t-0000398/
ldmud-3.3.719/test/t-0000548/
ldmud-3.3.719/test/t-030925/
ldmud-3.3.719/test/t-040413/
ldmud-3.3.719/test/t-041124/
ldmud-3.3.719/test/t-language/
/*------------------------------------------------------------------
 * Wrapper for the GnuTLS module.
 *
 *------------------------------------------------------------------
 */

#include "driver.h"
#include "machine.h"

#if defined(USE_TLS) && defined(HAS_GNUTLS)

#include <errno.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>

#if defined(HAVE_DIRENT_H) || defined(_POSIX_VERSION)
#    include <dirent.h>
#    define generic_dirent dirent
#    define DIRENT_NLENGTH(dirent) (strlen((dirent)->d_name))
#else /* not (DIRENT or _POSIX_VERSION) */
#    define generic_dirent direct
#    define DIRENT_NLENGTH(dirent) ((dirent)->d_namlen)
#    ifdef HAVE_SYS_NDIR_H
#        include <sys/ndir.h>
#    endif /* SYSNDIR */
#    ifdef HAVE_SYS_DIR_H
#        include <sys/dir.h>
#    endif /* SYSDIR */
#    ifdef HAVE_NDIR_H
#        include <ndir.h>
#    endif /* NDIR */
#endif /* not (HAVE_DIRENT_H or _POSIX_VERSION) */

#ifndef S_ISREG
#    define S_ISREG(m) (((m)&S_IFMT) == S_IFREG)
#endif


#include <gnutls/gnutls.h>
#include <gnutls/x509.h>

#include "pkg-tls.h"

#include "actions.h"
#include "array.h"
#include "comm.h"
#include "interpret.h"
#include "main.h"
#include "mstrings.h"
#include "object.h"
#include "sha1.h"
#include "svalue.h"
#include "xalloc.h"

#include "../mudlib/sys/tls.h"
/*-------------------------------------------------------------------------*/
/* Variables */

static Bool tls_is_available = MY_FALSE;
  /* Set to TRUE when the TLS layer has been initialised successfully.
   */

static gnutls_certificate_server_credentials x509_cred;
  /* The x509 credentials. */

static gnutls_dh_params dh_params;
  /* The Diffie-Hellmann parameters */

/*-------------------------------------------------------------------------*/
static int
generate_dh_params (void)

/* GnuTLS: Generate Diffie Hellman parameters and store them in the global
 * <dh_params>.  They are for use with DHE kx algorithms. These should be
 * discarded and regenerated once a day, once a week or once a month. Depends
 * on the security requirements.
 *
 * tls_is_available must be TRUE.
 */

{
#if HAS_GNUTLS_VERSION < 8
    gnutls_datum prime, generator;

    gnutls_dh_params_init( &dh_params);
    gnutls_dh_params_generate( &prime, &generator, DH_BITS);
    gnutls_dh_params_set( dh_params, prime, generator, DH_BITS);

    free( prime.data);
    free( generator.data);
#else
    gnutls_dh_params_init( &dh_params);
    gnutls_dh_params_generate2( dh_params, DH_BITS);
#endif
    return 0;
} /* generate_dh_params() */

/*-------------------------------------------------------------------------*/
static void
initialize_tls_session (gnutls_session *session)

/* GnuTLS: Initialise a TLS <session>.
 * tls_is_available must be TRUE.
 */

{
    gnutls_init(session, GNUTLS_SERVER);

    /* avoid calling all the priority functions, since the defaults
     * are adequate.
     */
    gnutls_set_default_priority( *session);   

    gnutls_credentials_set( *session, GNUTLS_CRD_CERTIFICATE, x509_cred);

    gnutls_certificate_server_set_request( *session, GNUTLS_CERT_REQUEST);

    gnutls_dh_set_prime_bits( *session, DH_BITS);
} /* initialize_tls_session() */

/*-------------------------------------------------------------------------*/
static void *
tls_xalloc (size_t size)

/* Wrapper function so that (gnu)tls will use the driver's allocator.
 * The wrapper is required as 'pxalloc' itself is a macro.
 */

{
    return pxalloc(size);
} /* tls_xalloc() */

/*-------------------------------------------------------------------------*/
static void *
tls_rexalloc (void *old, size_t size)

/* Wrapper function so that (gnu)tls will use the driver's allocator.
 * The wrapper is required as 'prexalloc' itself is a macro.
 */

{
    return prexalloc(old, size);
} /* tls_rexalloc() */

/*-------------------------------------------------------------------------*/
static void
tls_xfree (void *p)

/* Wrapper function so that (gnu)tls will use the driver's allocator.
 * The wrapper is not exactly required for pfree(),  but it keeps things
 * consistent.
 */

{
    return pfree(p);
} /* tls_xfree() */

/*-------------------------------------------------------------------------*/
void
tls_verify_init (void)

/* initialize or reinitialize tls certificate storage and revocation lists.
 */
{
    gnutls_certificate_free_cas(x509_cred);

    if (tls_trustfile != NULL)
    {
        int err;

        printf("%s TLS: (GnuTLS) trusted x509 certificates from '%s'.\n"
              , time_stamp(), tls_trustfile);
        debug_message("%s TLS: (GnuTLS) trusted x509 certificates from '%s'.\n"
                     , time_stamp(), tls_trustfile);
        err = gnutls_certificate_set_x509_trust_file(x509_cred, tls_trustfile, GNUTLS_X509_FMT_PEM);

        if (err < 0)
        {
            printf("%s TLS: Error setting x509 verification certificates: %s\n"
                  , time_stamp(), gnutls_strerror(err));
            debug_message("%s TLS: Error setting x509 verification certificates: %s\n"
                         , time_stamp(), gnutls_strerror(err));
        }
    }

    if (tls_trustdirectory)
    {
        DIR * d;
        char *fname;
        size_t dirlen;
        int err;

        printf("%s TLS: (GnuTLS) trusted x509 certificates from directory '%s'.\n"
              , time_stamp(), tls_trustdirectory);
        debug_message("%s TLS: (GnuTLS) trusted x509 certificates from directory '%s'.\n"
                     , time_stamp(), tls_trustdirectory);

        dirlen = strlen(tls_trustdirectory);
        fname = (char*) xalloc(dirlen + NAME_MAX + 2);
        if (!fname)
        {
            errno = ENOMEM;
            d = NULL;
        }
        else
        {
            strcpy(fname, tls_trustdirectory);
            fname[dirlen++] = '/';
            d = opendir(tls_trustdirectory);
        }

        if (d == NULL)
        {
            printf("%s TLS: Can't read trust directory: %s.\n"
                  , time_stamp(), strerror(errno));
            debug_message("%s TLS: Can't read trust directory: %s\n"
                         , time_stamp(), strerror(errno));
        }
        else
        {
            struct dirent *file;

            while ((file = readdir(d)) != NULL)
            {
                struct stat st;

                strcpy(fname+dirlen, file->d_name);
                stat(fname, &st);

                if (S_ISREG(st.st_mode))
                {
                    err = gnutls_certificate_set_x509_trust_file(x509_cred, fname, GNUTLS_X509_FMT_PEM);
                    if (err < 0)
                    {
                        printf("%s TLS: Error setting x509 verification certificates from '%s': %s\n"
                              , time_stamp(), fname, gnutls_strerror(err));
                        debug_message("%s TLS: Error setting x509 verification certificates from '%s': %s\n"
                                     , time_stamp(), fname, gnutls_strerror(err));
                    }
                }
            }

            closedir(d);
        }

        xfree(fname);
    }
    else if(tls_trustfile == NULL)
    {
        printf("%s TLS: (GnuTLS) Trusted x509 certificates locations not specified.\n"
              , time_stamp());
        debug_message("%s TLS: (GnuTLS) trusted x509 certificates locations not specified.\n"
                     , time_stamp());
    }

    gnutls_certificate_free_crls(x509_cred);
    if (tls_crlfile != NULL)
    {
        int err;

        printf("%s TLS: (GnuTLS) CRLs from '%s'.\n"
              , time_stamp(), tls_crlfile);
        debug_message("%s TLS: (GnuTLS) CRLs from '%s'.\n"
                     , time_stamp(), tls_crlfile);
        err = gnutls_certificate_set_x509_crl_file(x509_cred, tls_crlfile, GNUTLS_X509_FMT_PEM);

        if (err < 0)
        {
            printf("%s TLS: Error loading CRLs: %s\n"
                  , time_stamp(), gnutls_strerror(err));
            debug_message("%s TLS: Error loading CRLs: %s\n"
                         , time_stamp(), gnutls_strerror(err));
        }
    }

    if (tls_crldirectory)
    {
        DIR * d;
        char *fname;
        size_t dirlen;
        int err;

        printf("%s TLS: (GnuTLS) CRLs from directory '%s'.\n"
              , time_stamp(), tls_crldirectory);
        debug_message("%s TLS: (GnuTLS) CRLs from directory '%s'.\n"
                     , time_stamp(), tls_crldirectory);

        dirlen = strlen(tls_crldirectory);
        fname = (char*) xalloc(dirlen + NAME_MAX + 2);
        if (!fname)
        {
            errno = ENOMEM;
            d = NULL;
        }
        else
        {
            strcpy(fname, tls_crldirectory);
            fname[dirlen++] = '/';
            d = opendir(tls_crldirectory);
        }

        if (d == NULL)
        {
            printf("%s TLS: Can't read CRL directory: %s.\n"
                  , time_stamp(), strerror(errno));
            debug_message("%s TLS: Can't read CRL directory: %s\n"
                         , time_stamp(), strerror(errno));
        }
        else
        {
            struct dirent *file;

            while ((file = readdir(d)) != NULL)
            {
                struct stat st;

                strcpy(fname+dirlen, file->d_name);
                stat(fname, &st);

                if (S_ISREG(st.st_mode))
                {
                    err = gnutls_certificate_set_x509_crl_file(x509_cred, fname, GNUTLS_X509_FMT_PEM);
                    if (err < 0)
                    {
                        printf("%s TLS: Error loading CRL from '%s': %s\n"
                              , time_stamp(), fname, gnutls_strerror(err));
                        debug_message("%s TLS: Error loading CRL from '%s': %s\n"
                                     , time_stamp(), fname, gnutls_strerror(err));
                    }
                }
            }

            closedir(d);
        }

        xfree(fname);
    }
    else if(!tls_crlfile)
    {
        printf("%s TLS: (GnuTLS) CRL checking disabled.\n"
              , time_stamp());
        debug_message("%s TLS: (GnuTLS) CRL checking disabled.\n"
                     , time_stamp());
    }
}

/*-------------------------------------------------------------------------*/
void
tls_global_init (void)

/* Initialise the TLS package; to be called once at program startup.
 */

{
    int f;

    if (tls_keyfile == NULL)
    {
        printf("%s TLS deactivated.\n", time_stamp());
        return;
    }

    /* In order to be able to identify gnutls allocations as such, we redirect
     * all allocations through the driver's allocator. The wrapper functions
     * make sure that the allocations are annotated properly with this source
     * file.
     */
    gnutls_global_set_mem_functions(tls_xalloc,
                                    tls_xalloc,
                                    NULL,
                                    tls_rexalloc,
                                    tls_xfree);

    gnutls_global_init();

    gnutls_certificate_allocate_credentials(&x509_cred);

    printf("%s TLS: (GnuTLS) x509 keyfile '%s', certfile '%s'\n"
          , time_stamp(), tls_keyfile, tls_certfile);
    debug_message("%s TLS: (GnuTLS) Keyfile '%s', Certfile '%s'\n"
                 , time_stamp(), tls_keyfile, tls_certfile);

    f = gnutls_certificate_set_x509_key_file(x509_cred, tls_certfile, tls_keyfile, GNUTLS_X509_FMT_PEM);
    if (f < 0)
    {
        printf("%s TLS: Error setting x509 keyfile: %s\n"
              , time_stamp(), gnutls_strerror(f));
        debug_message("%s TLS: Error setting x509 keyfile: %s\n"
                     , time_stamp(), gnutls_strerror(f));
    }
    else
    {
        printf("%s TLS: x509 keyfile and certificate set.\n", time_stamp());

        tls_verify_init();

        generate_dh_params();

        gnutls_certificate_set_dh_params( x509_cred, dh_params);

        tls_is_available = MY_TRUE;
    }

} /* tls_global_init() */

/*-------------------------------------------------------------------------*/
void
tls_global_deinit (void)

/* Clean up the TLS package on program termination.
 */

{
    if (tls_is_available)
    {
        gnutls_certificate_free_credentials(x509_cred);
    }

    gnutls_global_deinit();

    tls_is_available = MY_FALSE;

} /* tls_global_deinit() */

/*-------------------------------------------------------------------------*/
int
tls_read (interactive_t *ip, char *buffer, int length)

/* Read up to <length> bytes data for the TLS connection of <ip>
 * and store it in <buffer>.
 * Return then number of bytes read, or a negative number if an error
 * occured.
 */

{
    int ret = -11;

    do {
           ret = gnutls_record_recv(ip->tls_session, buffer, length);
    } while ( ret < 0 && (ret == GNUTLS_E_INTERRUPTED || ret == GNUTLS_E_AGAIN) );

    if (ret == 0)
    {
        tls_deinit_connection(ip);
    }
    else if (ret < 0)
    {
        debug_message("%s GnuTLS: Error in receiving data (%s). "
                      "Closing the connection.\n"
                     , time_stamp(), gnutls_strerror(ret));
        tls_deinit_connection(ip);
    }

    return (ret < 0 ? -1 : ret);
} /* tls_read() */

/*-------------------------------------------------------------------------*/
int
tls_write (interactive_t *ip, char *buffer, int length)

/* Write <length> bytes from <buffer> to the TLS connection of <ip>
 * Return the number of bytes written, or a negative number if an error
 * occured.
 */

{
    int ret = -1;

    ret = gnutls_record_send( ip->tls_session, buffer, length );
    if (ret < 0)
    {
        /* Let comm.c handle EINTR and EWOULDBLOCK.
         * We are then called again later with the
         * same content.
         */
        if (ret == GNUTLS_E_INTERRUPTED)
        {
            errno = EINTR;
            return -1;
        }
        else if (ret == GNUTLS_E_AGAIN)
        {
            errno = EWOULDBLOCK;
            return -1;
        }

        debug_message("%s GnuTLS: Error in sending data (%s). "
                      "Closing the connection.\n"
                     , time_stamp(), gnutls_strerror(ret));
        tls_deinit_connection(ip);
    }

    return (ret<0 ? -1 : ret);
} /* tls_write() */

/*-------------------------------------------------------------------------*/
int
tls_do_handshake (interactive_t *ip)

/* Continue the TLS initialisation handshake for interactive <ip>.
 * Return a negative error code if the connection can not be set up.
 * Return 0 if the connection is still begin set up.
 * Return 1 if the connection is now active (or if no secure connection
 * had been requested).
 *
 * This function does only the package specific part of 
 * tls_continue_handshake.
 */

{
    int ret;

    ret = gnutls_handshake(ip->tls_session);

    if (ret != GNUTLS_E_AGAIN && ret != GNUTLS_E_INTERRUPTED)
    {
        if (ret < 0)
        {
            /* Setup failed */
            gnutls_deinit(ip->tls_session);
            ip->tls_session = NULL;
        }
        else
        {
            /* Setup finished */
            ret = 1;
        }
    }
    else
        ret = 0;

    return ret;
} /* tls_do_handshake() */

/*-------------------------------------------------------------------------*/
int
tls_init_connection (interactive_t *ip)

/* Starts a TLS secured connection to the interactive <ip>.
 * Returns a negative error code or 0 for success.
 *
 * After a successful start tls_do_handshake will be called.
 */

{
    initialize_tls_session(&ip->tls_session);
    gnutls_transport_set_ptr(ip->tls_session, (gnutls_transport_ptr)(ip->socket));

    return 0;
} /* tls_init_connection() */

/*-------------------------------------------------------------------------*/
vector_t *
tls_check_certificate (interactive_t *ip, Bool more)

/* Checks the certificate of the secured connection and returns
 * an array with the information about it for the efun
 * tls_check_certificate().
 */

{
    vector_t *v = NULL;
    const gnutls_datum_t *cert_list;
    gnutls_x509_crt_t cert;
#if LIBGNUTLS_VERSION_MAJOR > 1 || (LIBGNUTLS_VERSION_MAJOR==1 && (LIBGNUTLS_VERSION_MINOR > 7 || (LIBGNUTLS_VERSION_MINOR == 7 && LIBGNUTLS_VERSION_MINOR >= 8)))
#define GNUTLS_NEW_DN_API
    gnutls_x509_dn_t subject;
#endif
    unsigned int cert_list_size;
    unsigned int result;
    time_t t, now;
    int err;

    cert_list = gnutls_certificate_get_peers(ip->tls_session, &cert_list_size);
    if (cert_list == NULL || cert_list_size == 0)
        return v;

    err = gnutls_certificate_verify_peers2(ip->tls_session, &result);
    if (err < 0)
        return v;

    if (gnutls_x509_crt_init(&cert) < 0)
        return v;

    if (gnutls_x509_crt_import(cert, &cert_list[0], GNUTLS_X509_FMT_DER) < 0)
    {
        gnutls_x509_crt_deinit(cert);
        return v;
    }

    now = time(0);
    t = gnutls_x509_crt_get_expiration_time(cert);
    if (t == (time_t)-1 || t < now)
        result |= GNUTLS_CERT_INVALID;

    t = gnutls_x509_crt_get_activation_time(cert);
    if (t == (time_t)-1 || t >= now)
        result |= GNUTLS_CERT_INVALID;

    v = allocate_array(more ? 3 : 2);
    put_number(&(v->item[0]), result);

#ifdef GNUTLS_NEW_DN_API
    err = gnutls_x509_crt_get_subject(cert, &subject);
    if (err < 0)
        put_number(&(v->item[1]), 0);
    else
    {
        int count;
        int rdn, ava_nr;
        gnutls_x509_ava_st ava;
        vector_t *extra;

        count = 0;
        for(rdn=0; !gnutls_x509_dn_get_rdn_ava(subject, rdn, 0, &ava); rdn++)
        {
            count++;
            for(ava_nr=1; !gnutls_x509_dn_get_rdn_ava(subject, rdn, ava_nr, &ava); ava_nr++)
                count++;
        }

        extra = allocate_array(3 * count);
        count = 0;

        for(rdn=0; !gnutls_x509_dn_get_rdn_ava(subject, rdn, 0, &ava); rdn++)
            for(ava_nr=0; ava_nr==0 || !gnutls_x509_dn_get_rdn_ava(subject, rdn, ava_nr, &ava); ava_nr++)
            {
                put_c_n_string(&(extra->item[count++]), (char*)ava.oid.data, ava.oid.size-1);
                put_number(&(extra->item[count]), 0); count++;
                put_c_n_string(&(extra->item[count++]), (char*)ava.value.data, ava.value.size);
            }

        put_array(&(v->item[1]), extra);
    }
#else
    {
        int count, nr;
        vector_t *extra;
        char oid[128];
        char data[256];
        char *ptr;
        size_t osize, dsize;

        count = 0;
        nr = 0;

        while(1)
        {
            osize = sizeof(oid);

            err = gnutls_x509_crt_get_dn_oid(cert, nr, oid, &osize);
            if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
                break;
            if (err >= 0)
                count++;
            nr++;
        }

        extra = allocate_array(3 * count);
        count = 0;
        ptr = 0;
        nr = 0;

        while (1)
        {
            osize = sizeof(oid);

            err = gnutls_x509_crt_get_dn_oid(cert, nr, oid, &osize);
            if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
                break;
            if (err >= 0)
            {
                dsize = sizeof(data);
                ptr = NULL;

                err = gnutls_x509_crt_get_dn_by_oid(cert, oid, 0, 0, data, &dsize);
                if (err == GNUTLS_E_SHORT_MEMORY_BUFFER)
                {
                    ptr = (char*) xalloc(dsize);
                    if (ptr)
                        err = gnutls_x509_crt_get_dn_by_oid(cert, oid, 0, 0, ptr, &dsize);
                }

                put_c_n_string(&(extra->item[count++]), oid, osize);
                put_number(&(extra->item[count]), 0); count++;
                if (err >= 0)
                    put_c_n_string(&(extra->item[count++]), (ptr!=NULL) ? ptr : data, dsize);
                else
                {
                    put_number(&(extra->item[count]), 0); count++;
                }

                if (ptr)
                    xfree(ptr);
            }

            nr++;
        }

        put_array(&(v->item[1]), extra);
    }
#endif

    if (more)
    {
        int count, nr;
        vector_t *extra;
        char oid[128];
        char data[256];
        char *ptr;
        size_t osize, dsize;

        count = 0;
        nr = 0;

        while(1)
        {
            osize = sizeof(oid);

            err = gnutls_x509_crt_get_extension_oid(cert, nr, oid, &osize);
            if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
                break;
            if (err >= 0)
                count++;
            nr++;
        }

        extra = allocate_array(3 * count);
        count = 0;
        ptr = 0;
        nr = 0;

        while (1)
        {
            osize = sizeof(oid);

            err = gnutls_x509_crt_get_extension_oid(cert, nr, oid, &osize);
            if (err == GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE)
                break;
            if (err >= 0)
            {
#ifndef GNUTLS_NEW_DN_API
                unsigned int critical;
#endif
                dsize = sizeof(data);
                ptr = NULL;

#ifdef GNUTLS_NEW_DN_API
                err = gnutls_x509_crt_get_extension_data(cert, nr, data, &dsize);
                if (err == GNUTLS_E_SHORT_MEMORY_BUFFER)
                {
                    ptr = (char*) xalloc(dsize);
                    if (ptr)
                        err = gnutls_x509_crt_get_extension_data(cert, nr, ptr, &dsize);
                }
#else
                err = gnutls_x509_crt_get_extension_by_oid(cert, oid, 0, data, &dsize, &critical);
                if (err == GNUTLS_E_SHORT_MEMORY_BUFFER)
                {
                    ptr = (char*) xalloc(dsize);
                    if (ptr)
                        err = gnutls_x509_crt_get_extension_by_oid(cert, oid, 0, ptr, &dsize, &critical);
                }
#endif

                put_c_n_string(&(extra->item[count++]), oid, osize);
                put_number(&(extra->item[count]), 0); count++;
                if (err >= 0)
                    put_c_n_string(&(extra->item[count++]), (ptr!=NULL) ? ptr : data, dsize);
                else
                {
                    put_number(&(extra->item[count]), 0); count++;
                }

                if (ptr)
                    xfree(ptr);
            }

            nr++;
        }

        put_array(&(v->item[2]), extra);
    }

    gnutls_x509_crt_deinit(cert);
    return v;
} /* tls_check_certificate() */

/*-------------------------------------------------------------------------*/
void
tls_deinit_connection (interactive_t *ip)

/* Close the TLS connection for the interactive <ip> if there is one.
 */

{
    if (ip->tls_status != TLS_INACTIVE)
    {
        gnutls_bye( ip->tls_session, GNUTLS_SHUT_WR);
        gnutls_deinit(ip->tls_session);
        ip->tls_session = NULL;
    }

    if (ip->tls_cb != NULL)
    {
        free_callback(ip->tls_cb);
        xfree(ip->tls_cb);
        ip->tls_cb = NULL;
    }
    ip->tls_status = TLS_INACTIVE;
} /* tls_deinit_connection() */

/*-------------------------------------------------------------------------*/
const char *
tls_error(int err)

/* Returns a string describing the error behind the
 * error number <err>, which is always negative.
 */

{
    return gnutls_strerror(err);
} /* tls_error() */

/*-------------------------------------------------------------------------*/
vector_t *
tls_query_connection_info (interactive_t *ip)

/* Returns the connection info array for the efun
 * tls_query_connection_info(). <ip> is guaranteed
 * to have a TLS secured connection.
 */

{
    vector_t * rc;
    rc = allocate_array(TLS_INFO_MAX);

    put_number(&(rc->item[TLS_CIPHER])
              , gnutls_cipher_get(ip->tls_session));
    put_number(&(rc->item[TLS_COMP])
              , gnutls_compression_get(ip->tls_session));
    put_number(&(rc->item[TLS_KX])
              , gnutls_kx_get(ip->tls_session));
    put_number(&(rc->item[TLS_MAC])
              , gnutls_mac_get(ip->tls_session));
    put_number(&(rc->item[TLS_PROT])
              , gnutls_protocol_get_version(ip->tls_session));

    return rc;
} /* tls_query_connection_info() */

/*-------------------------------------------------------------------------*/
Bool
tls_available ()

/* If the global TLS Initialisation could not been set up, tls_available()
 * returns MY_FALSE, otherwise MY_TRUE.
 */

{
    return tls_is_available;
} /* tls_available() */


/***************************************************************************/
#endif /* USE_TLS && HAS_GNUTLS */