From: Nikos Mavrogiannopoulos Date: Sat, 26 Jan 2013 20:38:26 +0000 (+0100) Subject: updated sbuf interface. X-Git-Tag: gnutls_3_1_7~54 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=156e8489c22f04b92bb36c2cb2da32707f81d321;p=thirdparty%2Fgnutls.git updated sbuf interface. --- diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h index 5ab3ebddb8..2f367be4ef 100644 --- a/lib/gnutls_int.h +++ b/lib/gnutls_int.h @@ -107,6 +107,7 @@ typedef struct /* The maximum digest size of hash algorithms. */ +#define MAX_FILENAME 512 #define MAX_HASH_SIZE 64 #define MAX_CIPHER_BLOCK_SIZE 16 #define MAX_CIPHER_KEY_SIZE 32 diff --git a/lib/includes/gnutls/sbuf.h b/lib/includes/gnutls/sbuf.h index cb9100d0eb..d80df64621 100644 --- a/lib/includes/gnutls/sbuf.h +++ b/lib/includes/gnutls/sbuf.h @@ -3,9 +3,9 @@ * * This file is part of GnuTLS. * - * The GnuTLS is free software; you can redistribute it and/or + * The GnuTLS library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 3 of + * as published by the Free Software Foundation; either version 2.1 of * the License, or (at your option) any later version. * * This library is distributed in the hope that it will be useful, but @@ -25,6 +25,15 @@ /* Buffered session I/O */ typedef struct gnutls_sbuf_st *gnutls_sbuf_t; +typedef struct gnutls_credentials_st *gnutls_credentials_t; + +typedef enum +{ + GNUTLS_VMETHOD_INSECURE = 0, + GNUTLS_VMETHOD_TOFU = 1<<0, + GNUTLS_VMETHOD_SINGLE_CA = 1<<1, + GNUTLS_VMETHOD_SYSTEM_CAS = 1<<2 +} gnutls_vmethod_t; ssize_t gnutls_sbuf_printf (gnutls_sbuf_t sb, const char *fmt, ...) #ifdef __GNUC__ @@ -46,9 +55,24 @@ gnutls_sbuf_getdelim (gnutls_sbuf_t sbuf, char **lineptr, size_t *n, int delimit #define gnutls_sbuf_getline(sbuf, ptr, n) gnutls_sbuf_getdelim(sbuf, ptr, n, '\n') #define GNUTLS_SBUF_QUEUE_FLUSHES 1 -int gnutls_sbuf_init (gnutls_sbuf_t * sb, gnutls_session_t session, - unsigned int flags); void gnutls_sbuf_deinit(gnutls_sbuf_t sb); +int gnutls_sbuf_sinit (gnutls_sbuf_t * isb, gnutls_session_t session, + unsigned int flags); + +int gnutls_sbuf_client_init (gnutls_sbuf_t * isb, const char* hostname, + const char* service, + gnutls_transport_ptr fd, + const char* priority, gnutls_credentials_t cred, + unsigned int flags); + +/* High level credential structures */ +int gnutls_credentials_init (gnutls_credentials_t* cred); +void gnutls_credentials_deinit (gnutls_credentials_t cred); + +int gnutls_credentials_set_trust (gnutls_credentials_t cred, unsigned vflags, + const char* ca_file, const char* crl_file, + const char* tofu_file); + #endif /* GNUTLS_SBUF_H */ diff --git a/lib/sbuf.c b/lib/sbuf.c index 46e9ffb311..da645614a1 100644 --- a/lib/sbuf.c +++ b/lib/sbuf.c @@ -28,7 +28,7 @@ #include /** - * gnutls_init: + * gnutls_sbuf_sinit: * @isb: is a pointer to a #gnutls_sbuf_t structure. * @session: a GnuTLS session * @flags: should be zero or %GNUTLS_SBUF_QUEUE_FLUSHES @@ -42,8 +42,8 @@ * * Since: 3.1.7 **/ -int gnutls_sbuf_init (gnutls_sbuf_t * isb, gnutls_session_t session, - unsigned int flags) +int gnutls_sbuf_sinit (gnutls_sbuf_t * isb, gnutls_session_t session, + unsigned int flags) { struct gnutls_sbuf_st* sb; @@ -60,6 +60,306 @@ struct gnutls_sbuf_st* sb; return 0; } + +/** + * gnutls_credentials_deinit: + * @cred: is a #gnutls_credentials_t structure. + * + * This function deinitializes a #gnutls_credentials_t structure. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + * + * Since: 3.1.7 + **/ +void gnutls_credentials_deinit (gnutls_credentials_t cred) +{ + if (cred->xcred) + gnutls_certificate_free_credentials(cred->xcred); + gnutls_free(cred); +} + +/** + * gnutls_credentials_init: + * @cred: is a pointer to a #gnutls_credentials_t structure. + * + * This function initializes a #gnutls_credentials_t structure. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + * + * Since: 3.1.7 + **/ +int gnutls_credentials_init (gnutls_credentials_t* cred) +{ + *cred = gnutls_calloc(1, sizeof(*cred)); + if (*cred == NULL) + return gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + + return 0; +} + +static int +_verify_certificate_callback (gnutls_session_t session) +{ + unsigned int status; + gnutls_sbuf_t sb; + int ret, type; + const char *hostname = NULL; + const char *service = NULL; + const char *tofu_file = NULL; + + sb = gnutls_session_get_ptr(session); + if (sb == NULL) + return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR); + + if (sb->server_name[0] != 0) + hostname = sb->server_name; + + if (sb->service_name[0] != 0) + service = sb->service_name; + + if (sb->cred->tofu_file[0] != 0) + tofu_file = sb->cred->tofu_file; + + /* This verification function uses the trusted CAs in the credentials + * structure. So you must have installed one or more CA certificates. + */ + if (sb->cred->vflags & GNUTLS_VMETHOD_SYSTEM_CAS || sb->cred->vflags & GNUTLS_VMETHOD_SINGLE_CA) + { + ret = gnutls_certificate_verify_peers3 (session, hostname, &status); + if (ret < 0) + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); + + if (status != 0) /* Certificate is not trusted */ + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); + } + + if (sb->cred->vflags & GNUTLS_VMETHOD_TOFU) + { + const gnutls_datum_t *cert_list; + unsigned int cert_list_size; + + type = gnutls_certificate_type_get (session); + + /* Do SSH verification */ + cert_list = gnutls_certificate_get_peers (session, &cert_list_size); + if (cert_list == NULL) + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); + + /* service may be obtained alternatively using getservbyport() */ + ret = gnutls_verify_stored_pubkey(tofu_file, NULL, hostname, service, + type, &cert_list[0], 0); + if (ret == GNUTLS_E_NO_CERTIFICATE_FOUND) + { + /* host was not seen before. Store the key */ + gnutls_store_pubkey(tofu_file, NULL, hostname, service, + type, &cert_list[0], 0, 0); + } + else if (ret == GNUTLS_E_CERTIFICATE_KEY_MISMATCH) + return gnutls_assert_val(GNUTLS_E_CERTIFICATE_ERROR); + else if (ret < 0) + return gnutls_assert_val(ret); + } + + /* notify gnutls to continue handshake normally */ + return 0; +} + +/** + * gnutls_credentials_set_trust: + * @cred: is a #gnutls_credentials_t structure. + * @vflags: the requested peer verification methods + * @ca_file: a PEM file storing the certificate authority + * @crl_file: a PEM file storing any CRLs of the certificate authority + * @tofu_file: a file to store the trust on first use database + * + * This function initializes X.509 certificates in + * a #gnutls_credentials_t structure. + * + * The @ca_file and @crl_file are required only if @vflags includes + * %GNUTLS_VMETHOD_SINGLE_CA. The @tofu_file may be set if + * %GNUTLS_VMETHOD_TOFU is specified. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + * + * Since: 3.1.7 + **/ +int gnutls_credentials_set_trust (gnutls_credentials_t cred, unsigned vflags, + const char* ca_file, const char* crl_file, + const char* tofu_file) +{ +int ret; +unsigned len; + + if (cred->xcred == NULL) + { + ret = gnutls_certificate_allocate_credentials(&cred->xcred); + if (ret < 0) + return gnutls_assert_val(ret); + } + + if (vflags & GNUTLS_VMETHOD_SYSTEM_CAS) + { + ret = gnutls_certificate_set_x509_system_trust(cred->xcred); + if (ret < 0) + { + gnutls_assert(); + goto fail1; + } + } + + if (vflags & GNUTLS_VMETHOD_SINGLE_CA) + { + ret = gnutls_certificate_set_x509_trust_file(cred->xcred, ca_file, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + gnutls_assert(); + goto fail1; + } + + ret = gnutls_certificate_set_x509_crl_file(cred->xcred, crl_file, GNUTLS_X509_FMT_PEM); + if (ret < 0) + { + gnutls_assert(); + goto fail1; + } + } + + if (vflags & GNUTLS_VMETHOD_TOFU) + { + len = strlen(tofu_file); + + if (len >= sizeof(cred->tofu_file)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + memcpy(cred->tofu_file, tofu_file, len+1); + } + + gnutls_certificate_set_verify_function (cred->xcred, _verify_certificate_callback); + + return 0; +fail1: + gnutls_certificate_free_credentials(cred->xcred); + cred->xcred = NULL; + return ret; +} + + +/** + * gnutls_sbuf_client_init: + * @isb: is a pointer to a #gnutls_sbuf_t structure. + * @hostname: The name of the host to connect to + * @service: The name of the host to connect to + * @fd: a socket descriptor + * @priority: A priority string to use (use %NULL for default) + * @cred: A credentials structure + * @flags: should be zero or %GNUTLS_SBUF_QUEUE_FLUSHES + * + * This function initializes a #gnutls_sbuf_t structure associated + * with the provided session. If the flag %GNUTLS_SBUF_QUEUE_FLUSHES + * is set then gnutls_sbuf_queue() will flush when the maximum + * data size for a record is reached. + * + * Returns: %GNUTLS_E_SUCCESS on success, or an error code. + * + * Since: 3.1.7 + **/ +int gnutls_sbuf_client_init (gnutls_sbuf_t * isb, const char* hostname, + const char* service, + gnutls_transport_ptr fd, + const char* priority, gnutls_credentials_t cred, + unsigned int flags) +{ +struct gnutls_sbuf_st* sb; +gnutls_session_t session; +int ret; +unsigned len; + + ret = gnutls_init(&session, GNUTLS_CLIENT); + if (ret < 0) + return gnutls_assert_val(ret); + + sb = gnutls_calloc(1, sizeof(*sb)); + if (sb == NULL) + { + ret = gnutls_assert_val(GNUTLS_E_MEMORY_ERROR); + goto fail1; + } + _gnutls_buffer_init(&sb->buf); + sb->session = session; + sb->flags = flags; + + /* set session/handshake info + */ + gnutls_handshake_set_timeout(session, GNUTLS_DEFAULT_HANDSHAKE_TIMEOUT); + + ret = gnutls_priority_set_direct(session, priority, NULL); + if (ret < 0) + { + gnutls_assert(); + goto fail1; + } + + if (cred->xcred) + { + ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, cred->xcred); + if (ret < 0) + { + gnutls_assert(); + goto fail1; + } + } + + if (hostname) + { + len = strlen(hostname); + + if (len >= sizeof(sb->server_name)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + memcpy(sb->server_name, hostname, len+1); + + ret = gnutls_server_name_set(session, GNUTLS_NAME_DNS, hostname, len); + if (ret < 0) + { + gnutls_assert(); + goto fail1; + } + } + + if (service) + { + len = strlen(service); + + if (len >= sizeof(sb->service_name)) + return gnutls_assert_val(GNUTLS_E_INVALID_REQUEST); + memcpy(sb->service_name, service, len+1); + } + + gnutls_transport_set_ptr (session, fd); + + do + { + ret = gnutls_handshake(session); + } + while (ret < 0 && gnutls_error_is_fatal (ret) == 0); + + if (ret < 0) + { + gnutls_assert(); + goto fail1; + } + + *isb = sb; + gnutls_session_set_ptr( session, sb); + + return 0; + +fail1: + if (sb) + gnutls_sbuf_deinit(sb); + gnutls_deinit(session); + + return ret; +} + /** * gnutls_sbuf_deinit: * @sb: is a #gnutls_sbuf_t structure. diff --git a/lib/sbuf.h b/lib/sbuf.h index 70e8939791..44ea713604 100644 --- a/lib/sbuf.h +++ b/lib/sbuf.h @@ -2,10 +2,23 @@ # define SBUF_H #include +#include + +struct gnutls_credentials_st { + gnutls_certificate_credentials_t xcred; + char tofu_file[MAX_FILENAME]; + unsigned vflags; +}; struct gnutls_sbuf_st { gnutls_session_t session; gnutls_buffer_st buf; + + char server_name[MAX_SERVER_NAME_SIZE]; + char service_name[MAX_SERVER_NAME_SIZE]; + + gnutls_credentials_t cred; + unsigned int flags; }; diff --git a/lib/verify-tofu.c b/lib/verify-tofu.c index 4c19c90bc7..7e0eec76be 100644 --- a/lib/verify-tofu.c +++ b/lib/verify-tofu.c @@ -58,7 +58,6 @@ int store_pubkey(const char* db_name, const char* host, const char* service, time_t expiration, const gnutls_datum_t* pubkey); static int find_config_file(char* file, size_t max_size); -#define MAX_FILENAME 512 extern void *_gnutls_file_mutex;