diff options
author | joe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845> | 2004-10-04 21:20:31 +0000 |
---|---|---|
committer | joe <joe@61a7d7f5-40b7-0310-9c16-bb0ea8cb1845> | 2004-10-04 21:20:31 +0000 |
commit | e44d925681dfb819b10eef3d32ef9b54636af1f1 (patch) | |
tree | 88e37dfb24fadeb261afd908ccffc1d663bce236 | |
parent | b32b6401fdbbb484d2ed5e6650125ad169b99fc2 (diff) | |
download | neon-e44d925681dfb819b10eef3d32ef9b54636af1f1.tar.gz |
Begin integration of GNU TLS support from Aleix Conchillo Flaque:
* macros/neon.m4 (NE_CHECK_OPENSSLVER): Renamed from NE_CHECK_SSLVER.
(NEON_SSL): Add detection support for GNU TLS. Define HAVE_GNUTLS or
HAVE_OPENSSL as appropriate.
* src/ne_utils.c (version_string): Update to include GNU TLS version
string.
* src/ne_privssl.h (HAVE_GNUTLS): Add alternative private structure
definitions.
* src/ne_auth.c (get_cnonce): Adjust to use HAVE_OPENSSL rather than
NE_HAVE_SSL.
* src/ne_gnutls.c: New file.
git-svn-id: http://svn.webdav.org/repos/projects/neon/trunk@274 61a7d7f5-40b7-0310-9c16-bb0ea8cb1845
-rw-r--r-- | AUTHORS | 1 | ||||
-rw-r--r-- | macros/neon.m4 | 46 | ||||
-rw-r--r-- | src/ne_auth.c | 6 | ||||
-rw-r--r-- | src/ne_gnutls.c | 423 | ||||
-rw-r--r-- | src/ne_privssl.h | 36 | ||||
-rw-r--r-- | src/ne_utils.c | 13 |
6 files changed, 504 insertions, 21 deletions
@@ -3,3 +3,4 @@ Portions are: Copyright (C) 1999-2000 Tommi Komulainen <Tommi.Komulainen@iki.fi> Copyright (C) 1999-2000 Peter Boos <pedib@colorfullife.com> Copyright (C) 1991, 1995, 1996, 1997 Free Software Foundation, Inc. +Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org> diff --git a/macros/neon.m4 b/macros/neon.m4 index d077f6b..f037db5 100644 --- a/macros/neon.m4 +++ b/macros/neon.m4 @@ -1,4 +1,5 @@ # Copyright (C) 1998-2004 Joe Orton <joe@manyfish.co.uk> -*- autoconf -*- +# Copyright (C) 2004 Aleix Conchillo Flaque <aleix@member.fsf.org> # # This file is free software; you may copy and/or distribute it with # or without modifications, as long as this notice is preserved. @@ -759,9 +760,9 @@ AC_CHECK_FUNCS(snprintf vsnprintf,,[ break ])]) -dnl Usage: NE_CHECK_SSLVER(variable, version-string, version-hex) +dnl Usage: NE_CHECK_OPENSSLVER(variable, version-string, version-hex) dnl Define 'variable' to 'yes' if OpenSSL version is >= version-hex -AC_DEFUN([NE_CHECK_SSLVER], [ +AC_DEFUN([NE_CHECK_OPENSSLVER], [ AC_CACHE_CHECK([OpenSSL version is >= $2], $1, [ AC_EGREP_CPP(good, [#include <openssl/opensslv.h> #if OPENSSL_VERSION_NUMBER >= $3 @@ -794,17 +795,22 @@ else fi fi]) -dnl Check for OpenSSL +dnl Check for an SSL library (GNU TLS or OpenSSL) AC_DEFUN([NEON_SSL], [ -AC_ARG_WITH(ssl, AS_HELP_STRING([--with-ssl], [enable OpenSSL support])) +AC_ARG_WITH(ssl, + AS_HELP_STRING([--with-ssl=openssl|gnutls], + [enable SSL support (default OpenSSL)])) AC_ARG_WITH(egd, [[ --with-egd[=PATH] enable EGD support [using EGD socket at PATH]]]) case $with_ssl in -yes) - +/*) + AC_MSG_NOTICE([to use SSL libraries in non-standard locations, try --with-ssl --with-libs=$with_ssl]) + AC_MSG_ERROR([--with-ssl does not take a path argument]) + ;; +yes|openssl) NE_PKG_CONFIG(NE_SSL, openssl, [AC_MSG_NOTICE(using SSL library configuration from pkg-config) CPPFLAGS="$CPPFLAGS ${NE_SSL_CFLAGS}" @@ -817,13 +823,13 @@ yes) [AC_MSG_ERROR([OpenSSL headers not found, cannot enable SSL support])]) # Enable EGD support if using 0.9.7 or newer - NE_CHECK_SSLVER(ne_cv_lib_ssl097, 0.9.7, 0x00907000L) + NE_CHECK_OPENSSLVER(ne_cv_lib_ssl097, 0.9.7, 0x00907000L) if test "$ne_cv_lib_ssl097" = "yes"; then AC_MSG_NOTICE([OpenSSL >= 0.9.7; EGD support not needed in neon]) NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using OpenSSL (0.9.7 or later)]) else # Fail if OpenSSL is older than 0.9.6 - NE_CHECK_SSLVER(ne_cv_lib_ssl096, 0.9.6, 0x00906000L) + NE_CHECK_OPENSSLVER(ne_cv_lib_ssl096, 0.9.6, 0x00906000L) if test "$ne_cv_lib_ssl096" != "yes"; then AC_MSG_ERROR([OpenSSL 0.9.6 or later is required]) fi @@ -849,8 +855,32 @@ yes) fi fi + AC_DEFINE([HAVE_OPENSSL], 1, [Define if OpenSSL support is enabled]) NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_openssl" ;; +gnutls) + AC_PATH_PROG(GNUTLS_CONFIG, libgnutls-config, no) + + if test "$GNUTLS_CONFIG" = "no"; then + AC_MSG_ERROR([could not find libgnutls-config in \$PATH]) + fi + + ne_gnutls_ver=`$GNUTLS_CONFIG --version` + case $ne_gnutls_ver in + 1.*) ;; + *) AC_MSG_ERROR([GNU TLS major version "$ne_gnutls_ver" not supported]) ;; + esac + + CPPFLAGS="$CPPFLAGS `$GNUTLS_CONFIG --cflags`" + + AC_CHECK_HEADER([gnutls/gnutls.h],, + [AC_MSG_ERROR([could not find gnutls/gnutls.h in include path])]) + + NE_ENABLE_SUPPORT(SSL, [SSL support enabled, using GnuTLS $ne_gnutls_ver]) + NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_gnutls" + NEON_LIBS="$NEON_LIBS `$GNUTLS_CONFIG --libs`" + AC_DEFINE([HAVE_GNUTLS], 1, [Define if GnuTLS support is enabled]) + ;; *) # Default to off; only create crypto-enabled binaries if requested. NE_DISABLE_SUPPORT(SSL, [SSL support is not enabled]) NEON_EXTRAOBJS="$NEON_EXTRAOBJS ne_stubssl" diff --git a/src/ne_auth.c b/src/ne_auth.c index 52a137c..84eef89 100644 --- a/src/ne_auth.c +++ b/src/ne_auth.c @@ -50,7 +50,7 @@ #include <windows.h> /* for GetCurrentThreadId() etc */ #endif -#ifdef NE_HAVE_SSL +#ifdef HAVE_OPENSSL #include <openssl/rand.h> #endif @@ -241,7 +241,7 @@ static char *get_cnonce(void) ne_md5_init_ctx(&hash); -#ifdef NE_HAVE_SSL +#ifdef HAVE_OPENSSL if (RAND_status() == 1 && RAND_pseudo_bytes(data, sizeof data) >= 0) ne_md5_process_bytes(data, sizeof data, &hash); else { @@ -274,7 +274,7 @@ static char *get_cnonce(void) ne_md5_process_bytes(&pid, sizeof pid, &hash); } -#ifdef NE_HAVE_SSL +#ifdef HAVE_OPENSSL } #endif diff --git a/src/ne_gnutls.c b/src/ne_gnutls.c new file mode 100644 index 0000000..6b0e551 --- /dev/null +++ b/src/ne_gnutls.c @@ -0,0 +1,423 @@ +/* + neon SSL/TLS support using GNU TLS + Copyright (C) 2002-2004, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2004, Aleix Conchillo Flaque <aleix@member.fsf.org> + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Library General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library 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 + Library General Public License for more details. + + You should have received a copy of the GNU Library General Public + License along with this library; if not, write to the Free + Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, + MA 02111-1307, USA + +*/ + +#include "config.h" + +#include <sys/mman.h> +#include <sys/stat.h> +#include <sys/types.h> + +#ifdef HAVE_STRING_H +#include <string.h> +#endif + +#include <stdio.h> + +#include <gnutls/gnutls.h> +#include <gnutls/pkcs12.h> + +#include "ne_ssl.h" +#include "ne_string.h" +#include "ne_session.h" +#include "ne_i18n.h" + +#include "ne_private.h" +#include "ne_privssl.h" + +struct ne_ssl_dname_s { + char *dn; +}; + +struct ne_ssl_certificate_s { + ne_ssl_dname subj_dn, issuer_dn; + gnutls_x509_crt subject; + ne_ssl_certificate *issuer; + char *identity; +}; + +struct ne_ssl_client_cert_s { + gnutls_pkcs12 p12; + int decrypted; /* non-zero if successfully decrypted. */ + ne_ssl_certificate cert; + char *friendly_name; +}; + +char *ne_ssl_readable_dname(const ne_ssl_dname *name) +{ + return name->dn; +} + +int ne_ssl_dname_cmp(const ne_ssl_dname *dn1, const ne_ssl_dname *dn2) +{ + return strcmp(dn1->dn, dn2->dn); +} + +void ne_ssl_clicert_free(ne_ssl_client_cert *cc) +{ +#warning incomplete +} + +void ne_ssl_cert_validity(const ne_ssl_certificate *cert, + char *from, char *until) +{ + if (from) { + time_t t = gnutls_x509_crt_get_activation_time(cert->subject); + strftime(from, NE_SSL_VDATELEN, "%b %d %H:%M:%S %Y%Z", localtime(&t)); + } + if (until) { + time_t t = gnutls_x509_crt_get_expiration_time(cert->subject); + strftime(from, NE_SSL_VDATELEN, "%b %d %H:%M:%S %Y%Z", localtime(&t)); + } +} + +/* Returns a new buffer with X509 subject's (or issuer) distinguished name. */ +static char *x509_get_dn(gnutls_x509_crt x5, int subject) +{ + int ret, len; + char *dn; + + if (subject) + ret = gnutls_x509_crt_get_dn(x5, NULL, &len); + else + ret = gnutls_x509_crt_get_issuer_dn(x5, NULL, &len); + + if (ret < 0) + return NULL; + + dn = ne_malloc(len); + if (subject) + gnutls_x509_crt_get_dn(x5, dn, &len); + else + gnutls_x509_crt_get_issuer_dn(x5, dn, &len); + + return dn; +} + +/* Populate an ne_ssl_certificate structure from an X509 object. */ +static ne_ssl_certificate *populate_cert(ne_ssl_certificate *cert, + gnutls_x509_crt x5) +{ + cert->subj_dn.dn = x509_get_dn(x5, 1); + cert->issuer_dn.dn = x509_get_dn(x5, 0); + cert->issuer = NULL; + cert->subject = x5; + cert->identity = NULL; + return cert; +} + +void ne_ssl_set_clicert(ne_session *sess, const ne_ssl_client_cert *cc) +{ +#warning incomplete +} + +ne_ssl_context *ne_ssl_context_create(void) +{ + ne_ssl_context *ctx = ne_malloc(sizeof *ctx); + + gnutls_dh_params_init(&ctx->dh_params); + gnutls_dh_params_generate2(ctx->dh_params, 1024); + + gnutls_rsa_params_init(&ctx->rsa_params); + gnutls_rsa_params_generate2(ctx->rsa_params, 1024); + + gnutls_certificate_allocate_credentials(&ctx->cred.cert); + gnutls_certificate_set_dh_params(ctx->cred.cert, ctx->dh_params); + gnutls_certificate_set_rsa_params(ctx->cred.cert, ctx->rsa_params); + + return ctx; +} + +void ne_ssl_context_destroy(ne_ssl_context *ctx) +{ + if (ctx->sess) + gnutls_deinit(ctx->sess); + if (ctx->dh_params) + gnutls_dh_params_deinit(ctx->dh_params); + if (ctx->rsa_params) + gnutls_rsa_params_deinit(ctx->rsa_params); + if (ctx->cred.cert) + gnutls_certificate_free_credentials(ctx->cred.cert); + ne_free(ctx); +} + +/* For internal use only. */ +int ne_negotiate_ssl(ne_request *req) +{ + ne_session *sess = ne_get_session(req); + ne_ssl_context *ctx = sess->ssl_context; + const gnutls_datum *chain; + unsigned int chain_size; + + NE_DEBUG(NE_DBG_SSL, "Doing SSL negotiation.\n"); + + if (ne_sock_connect_ssl(sess->socket, ctx)) { + if (ctx->sess) { + /* remove cached session. */ + gnutls_deinit(ctx->sess); + ctx->sess = NULL; + } + ne_set_error(sess, _("SSL negotiation failed: %s"), + ne_sock_error(sess->socket)); + return NE_ERROR; + } + + return NE_OK; +} + +const ne_ssl_dname *ne_ssl_cert_issuer(const ne_ssl_certificate *cert) +{ + return &cert->issuer_dn; +} + +const ne_ssl_dname *ne_ssl_cert_subject(const ne_ssl_certificate *cert) +{ + return &cert->subj_dn; +} + +const ne_ssl_certificate *ne_ssl_cert_signedby(const ne_ssl_certificate *cert) +{ + return cert->issuer; +} + +const char *ne_ssl_cert_identity(const ne_ssl_certificate *cert) +{ + return cert->identity; +} + +void ne_ssl_ctx_trustcert(ne_ssl_context *ctx, const ne_ssl_certificate *cert) +{ +#warning incomplete +} + +void ne_ssl_trust_default_ca(ne_session *sess) +{ +#warning incomplete +} + +/* Functions from GNU TLS manual examples. + * + * Helper functions to load a certificate and key + * files into memory. They use mmap for simplicity. + */ +static gnutls_datum mmap_file(const char* filename) +{ + int fd; + gnutls_datum mmaped_file = { NULL, 0 }; + struct stat stat_st; + void* ptr; + + fd = open(filename, 0); + if (fd == -1) + return mmaped_file; + + fstat(fd, &stat_st); + + if ((ptr = mmap(NULL, stat_st.st_size, + PROT_READ, MAP_SHARED, fd, 0)) == MAP_FAILED) + return mmaped_file; + + mmaped_file.data = ptr; + mmaped_file.size = stat_st.st_size; + + return mmaped_file; +} + +static void munmap_file( gnutls_datum data) +{ + munmap(data.data, data.size); +} + +ne_ssl_client_cert *ne_ssl_clicert_read(const char *filename) +{ + return NULL; +} + +int ne_ssl_clicert_encrypted(const ne_ssl_client_cert *cc) +{ + return !cc->decrypted; +} + +int ne_ssl_clicert_decrypt(ne_ssl_client_cert *cc, const char *password) +{ + return 0; +} + +const ne_ssl_certificate *ne_ssl_clicert_owner(const ne_ssl_client_cert *cc) +{ + return &cc->cert; +} + +const char *ne_ssl_clicert_name(ne_ssl_client_cert *ccert) +{ + return ccert->friendly_name; +} + +ne_ssl_certificate *ne_ssl_cert_read(const char *filename) +{ + int ret; + gnutls_datum data; + gnutls_x509_crt x5; + + data = mmap_file(filename); + if (data.data == NULL) + return NULL; + + if (gnutls_x509_crt_init(&x5) != 0) + return NULL; + + ret = gnutls_x509_crt_import(x5, &data, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + gnutls_x509_crt_deinit(x5); + return NULL; + } + + return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), x5); +} + +int ne_ssl_cert_write(const ne_ssl_certificate *cert, const char *filename) +{ + int ret; + unsigned char buffer[10*1024]; + int len = sizeof buffer; + + FILE *fp = fopen(filename, "w"); + + if (fp == NULL) return -1; + + if (gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_PEM, buffer, + &len) < 0) { + fclose(fp); + return -1; + } + + if (fwrite(buffer, len, 1, fp) != 1) { + fclose(fp); + return -1; + } + + if (fclose(fp) != 0) + return -1; + + return 0; +} + +void ne_ssl_cert_free(ne_ssl_certificate *cert) +{ + gnutls_x509_crt_deinit(cert->subject); + if (cert->subj_dn.dn) ne_free(cert->subj_dn.dn); + if (cert->issuer_dn.dn) ne_free(cert->issuer_dn.dn); + if (cert->identity) ne_free(cert->identity); + + if (cert->issuer) + ne_ssl_cert_free(cert->issuer); + if (cert->identity) + ne_free(cert->identity); + ne_free(cert); +} + +int ne_ssl_cert_cmp(const ne_ssl_certificate *c1, const ne_ssl_certificate *c2) +{ + int ret1, ret2; + char digest1[100], digest2[100]; + + ret1 = ne_ssl_cert_digest(c1, digest1); + ret2 = ne_ssl_cert_digest(c2, digest2); + + if (ret1 < 0 || ret2 < 0) + return -1; + + return strcmp(digest1, digest2); +} + +/* The certificate import/export format is the base64 encoding of the + * raw DER; PEM without the newlines and wrapping. */ + +ne_ssl_certificate *ne_ssl_cert_import(const char *data) +{ + int ret; + size_t len; + unsigned char *der; + gnutls_datum buffer = { NULL, 0 }; + gnutls_x509_crt x5; + + if (gnutls_x509_crt_init(&x5) != 0) + return NULL; + + /* decode the base64 to get the raw DER representation */ + len = ne_unbase64(data, &der); + if (len == 0) return NULL; + + buffer.data = der; + buffer.size = len; + + ret = gnutls_x509_crt_import(x5, &buffer, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + gnutls_x509_crt_deinit(x5); + return NULL; + } + + return populate_cert(ne_calloc(sizeof(struct ne_ssl_certificate_s)), x5); +} + +char *ne_ssl_cert_export(const ne_ssl_certificate *cert) +{ + int ret; + unsigned char der[10*1024]; + int len = sizeof der; + + /* find the length of the DER encoding. */ + ret = gnutls_x509_crt_export(cert->subject, GNUTLS_X509_FMT_DER, der, &len); + if (ret < 0) + return NULL; + + return ne_base64(der, len); +} + +int ne_ssl_cert_digest(const ne_ssl_certificate *cert, char *digest) +{ + int ret; + size_t len; + unsigned char *sha1; + unsigned int j; + char *p; + + ret = gnutls_x509_crt_get_fingerprint(cert->subject, GNUTLS_DIG_SHA, + NULL, &len); + if (ret < 0 || len != 20) + return -1; + + sha1 = (unsigned char*) gnutls_malloc(len); + if (sha1 == NULL) + return -1; + + gnutls_x509_crt_get_fingerprint(cert->subject, GNUTLS_DIG_SHA, + sha1, &len); + + for (j = 0, p = digest; j < 20; j++) { + *p++ = NE_HEX2ASC((sha1[j] >> 4) & 0x0f); + *p++ = NE_HEX2ASC(sha1[j] & 0x0f); + *p++ = ':'; + } + + *--p = '\0'; + return 0; +} diff --git a/src/ne_privssl.h b/src/ne_privssl.h index ea3a840..2340c5f 100644 --- a/src/ne_privssl.h +++ b/src/ne_privssl.h @@ -1,6 +1,7 @@ /* SSL interface definitions internal to neon. - Copyright (C) 2003, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2003, 2004, Joe Orton <joe@manyfish.co.uk> + Copyright (C) 2004, Aleix Conchillo Flaque <aleix@member.fsf.org> This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public @@ -25,15 +26,15 @@ #ifndef NE_PRIVSSL_H #define NE_PRIVSSL_H -/* This is the private interface between ne_socket and ne_openssl. */ +/* This is the private interface between ne_socket, ne_gnutls and + * ne_openssl. */ -#ifdef NE_HAVE_SSL +#include "ne_ssl.h" -#include <openssl/ssl.h> +#ifdef HAVE_OPENSSL -#include "ne_ssl.h" +#include <openssl/ssl.h> -/* SSL context */ struct ne_ssl_context_s { SSL_CTX *ctx; SSL_SESSION *sess; @@ -43,6 +44,27 @@ struct ne_ssl_socket_s { SSL *ssl; }; -#endif /* NE_HAVE_SSL */ +#endif /* HAVE_OPENSSL */ + +#ifdef HAVE_GNUTLS + +#include <gnutls/gnutls.h> + +struct ne_ssl_context_s { + gnutls_session sess; + gnutls_dh_params dh_params; + gnutls_rsa_params rsa_params; + /* Use union in case other credentials want to be added + * (anonymous, SRP) */ + union { + gnutls_certificate_credentials cert; + } cred; +}; + +struct ne_ssl_socket_s { + gnutls_session sess; +}; + +#endif /* HAVE_GNUTLS */ #endif diff --git a/src/ne_utils.c b/src/ne_utils.c index 1df2414..9b22f09 100644 --- a/src/ne_utils.c +++ b/src/ne_utils.c @@ -34,10 +34,14 @@ #include <zlib.h> #endif -#ifdef NE_HAVE_SSL +#ifdef HAVE_OPENSSL #include <openssl/opensslv.h> #endif +#ifdef HAVE_GNUTLS +#include <gnutls/gnutls.h> +#endif + /* libxml2: pick up the version string. */ #if defined(HAVE_LIBXML) #include <libxml/xmlversion.h> @@ -107,13 +111,16 @@ static const char version_string[] = "neon " NEON_VERSION ": " #ifdef NE_HAVE_IDNA ", IDNA" #endif -#ifdef NE_HAVE_SSL +#ifdef HAVE_OPENSSL #ifdef OPENSSL_VERSION_TEXT ", " OPENSSL_VERSION_TEXT #else "OpenSSL (unknown version)" #endif /* OPENSSL_VERSION_TEXT */ -#endif +#endif /* HAVE_OPENSSL */ +#ifdef HAVE_GNUTLS + ", GNU TLS " LIBGNUTLS_VERSION +#endif /* HAVE_GNUTLS */ "." ; |