the upcoming 2.0 version
https://redmine.lighttpd.net/projects/lighttpd2
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1024 lines
33 KiB
1024 lines
33 KiB
/* |
|
* mod_gnutls - ssl support |
|
* |
|
* Description: |
|
* mod_gnutls listens on separate sockets for ssl connections (https://...) |
|
* |
|
* Setups: |
|
* gnutls - setup a ssl socket; takes a hash/key-value list of following parameters: |
|
* listen - (mandatory) the socket address (same as standard listen) |
|
* pemfile - (mandatory) contains key and certificate and intermediate certificates ("chain") for the key (PEM format) |
|
* priority - contains priority string (specifying ciphers and gnutls options), default: "NORMAL" |
|
* protect-against-beast - whether to append ":-CIPHER-ALL:+ARCFOUR-128" for SSL3/TLS1.0 connections to priority |
|
* dh-params - file with genereated dh-params. default: pre generated 4096-bit params included in the source |
|
* session-db-size - size of session db (TLS session cookies). set to <= 0 to disable. default: 256 |
|
* when SNI was enabled |
|
* sni-backend - "fetch" backend name to search certificates in with the SNI servername as key |
|
* sni-fallback-pemfile - certificate to use if request contained SNI servername, but the sni-backend didn't find anything |
|
* if request didn't contain SNI the standard "pemfile"(s) are used |
|
* NOTES: |
|
* * gnutls has some SNI support builtin - you can just load all certificates with multiple "pemfile" parameters, |
|
* and gnutls will try to pick the right one. |
|
* * listen and pemfile can be specified more than once |
|
* * certificates in a file have to be ordered from bottom to top (each certificate is followed by the one that signed it) |
|
* |
|
* Example config: |
|
* setup gnutls ( "listen" => "0.0.0.0:8443", "listen" => "[::]:8443", "pemfile" => "server.pem" ]; |
|
* |
|
* setup { |
|
* fetch.files_static "sni" => "/etc/lighttpd2/certs/sni_*_server.pem"; |
|
* gnutls ( "listen" => "0.0.0.0:8443", "listen" => "[::]:8443", "pemfile" => "server.pem", "sni-backend" => "sni" ); |
|
* } |
|
* |
|
* TODO: |
|
* * support client certificate authentication: http://www.gnutls.org/manual/gnutls.html#Client-certificate-authentication |
|
* gnutls_certificate_set_x509_system_trust (available since 3.0 (docs) or 3.0.19 (weechat ??)) |
|
* gnutls_certificate_set_x509_trust_file |
|
* * TLS session tickets are always activated with gnutls >= 2.10 - option to disable |
|
* * OCSP stapling |
|
* |
|
* Author: |
|
* Copyright (c) 2013 Stefan Bühler |
|
*/ |
|
|
|
#include <lighttpd/base.h> |
|
#include <lighttpd/throttle.h> |
|
|
|
#include "gnutls_filter.h" |
|
#include "ssl_client_hello_parser.h" |
|
#include "ssl-session-db.h" |
|
|
|
#include <gnutls/gnutls.h> |
|
#include <glib-2.0/glib/galloca.h> |
|
|
|
#if GNUTLS_VERSION_NUMBER >= 0x020a00 |
|
#define HAVE_SESSION_TICKET |
|
#endif |
|
|
|
LI_API gboolean mod_gnutls_init(liModules *mods, liModule *mod); |
|
LI_API gboolean mod_gnutls_free(liModules *mods, liModule *mod); |
|
|
|
static int load_dh_params_4096(gnutls_dh_params_t *dh_params); |
|
|
|
typedef struct mod_connection_ctx mod_connection_ctx; |
|
typedef struct mod_context mod_context; |
|
|
|
struct mod_connection_ctx { |
|
gnutls_session_t session; |
|
liConnection *con; |
|
mod_context *ctx; |
|
|
|
liGnuTLSFilter *tls_filter; |
|
|
|
liIOStream *sock_stream; |
|
gpointer simple_socket_data; |
|
|
|
liStream *client_hello_stream; |
|
#ifdef USE_SNI |
|
liJob sni_job; |
|
liJobRef *sni_jobref; |
|
liFetchEntry *sni_entry; |
|
liFetchWait *sni_db_wait; |
|
GString *sni_server_name; |
|
#endif |
|
}; |
|
|
|
struct mod_context { |
|
gint refcount; |
|
|
|
liSSLSessionDB *session_db; |
|
|
|
gnutls_certificate_credentials_t server_cert; |
|
gnutls_dh_params_t dh_params; |
|
gnutls_priority_t server_priority; |
|
gnutls_priority_t server_priority_beast; |
|
#ifdef HAVE_SESSION_TICKET |
|
gnutls_datum_t ticket_key; |
|
#endif |
|
|
|
#ifdef USE_SNI |
|
liFetchDatabase *sni_db, *sni_backend_db; |
|
gnutls_certificate_credentials_t sni_fallback_cert; |
|
#endif |
|
|
|
unsigned int protect_against_beast:1; |
|
}; |
|
|
|
#ifdef USE_SNI |
|
typedef struct fetch_cert_backend_lookup fetch_cert_backend_lookup; |
|
struct fetch_cert_backend_lookup { |
|
liFetchWait *wait; |
|
liFetchEntry *entry; |
|
mod_context *ctx; |
|
}; |
|
#endif |
|
|
|
static void mod_gnutls_context_release(mod_context *ctx); |
|
static void mod_gnutls_context_acquire(mod_context *ctx); |
|
|
|
|
|
#ifdef USE_SNI |
|
static gnutls_certificate_credentials_t creds_from_gstring(mod_context *ctx, GString *str) { |
|
gnutls_certificate_credentials_t creds = NULL; |
|
gnutls_datum_t pemfile; |
|
int r; |
|
|
|
if (NULL == str) return NULL; |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_allocate_credentials(&creds))) return NULL; |
|
|
|
pemfile.data = (unsigned char*) str->str; |
|
pemfile.size = str->len; |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_set_x509_key_mem(creds, &pemfile, &pemfile, GNUTLS_X509_FMT_PEM))) { |
|
goto error_free_creds; |
|
} |
|
|
|
gnutls_certificate_set_dh_params(creds, ctx->dh_params); |
|
|
|
return creds; |
|
|
|
error_free_creds: |
|
gnutls_certificate_free_credentials(creds); |
|
return NULL; |
|
} |
|
|
|
static void fetch_cert_backend_ready(gpointer data) { |
|
liFetchEntry *be; |
|
fetch_cert_backend_lookup *lookup = (fetch_cert_backend_lookup*) data; |
|
|
|
be = li_fetch_get2(lookup->ctx->sni_backend_db, lookup->entry->key, fetch_cert_backend_ready, lookup, &lookup->wait); |
|
if (NULL != be) { |
|
liFetchEntry *entry = lookup->entry; |
|
mod_context *ctx = lookup->ctx; |
|
g_slice_free(fetch_cert_backend_lookup, lookup); |
|
|
|
entry->backend_data = be; |
|
entry->data = creds_from_gstring(ctx, (GString*) be->data); |
|
li_fetch_entry_ready(entry); |
|
} |
|
} |
|
|
|
static void fetch_cert_lookup(liFetchDatabase* db, gpointer data, liFetchEntry *entry) { |
|
fetch_cert_backend_lookup *lookup = g_slice_new0(fetch_cert_backend_lookup); |
|
UNUSED(db); |
|
|
|
lookup->ctx = (mod_context*) data; |
|
lookup->entry = entry; |
|
fetch_cert_backend_ready(lookup); |
|
} |
|
static gboolean fetch_cert_revalidate(liFetchDatabase* db, gpointer data, liFetchEntry *entry) { |
|
UNUSED(db); UNUSED(data); |
|
return li_fetch_entry_revalidate((liFetchEntry*) entry->backend_data); |
|
} |
|
static void fetch_cert_refresh(liFetchDatabase* db, gpointer data, liFetchEntry *cur_entry, liFetchEntry *new_entry) { |
|
UNUSED(db); UNUSED(data); |
|
li_fetch_entry_refresh((liFetchEntry*) cur_entry->backend_data); |
|
li_fetch_entry_refresh_skip(new_entry); |
|
} |
|
static void fetch_cert_free_entry(gpointer data, liFetchEntry *entry) { |
|
gnutls_certificate_credentials_t creds = entry->data; |
|
UNUSED(data); |
|
|
|
if (NULL != creds) gnutls_certificate_free_credentials(creds); |
|
li_fetch_entry_release((liFetchEntry*) entry->backend_data); |
|
} |
|
static void fetch_cert_free_db(gpointer data) { |
|
mod_context *ctx = (mod_context*) data; |
|
mod_gnutls_context_release(ctx); |
|
} |
|
|
|
|
|
static const liFetchCallbacks fetch_cert_callbacks = { |
|
fetch_cert_lookup, |
|
fetch_cert_revalidate, |
|
fetch_cert_refresh, |
|
fetch_cert_free_entry, |
|
fetch_cert_free_db |
|
}; |
|
#endif |
|
|
|
|
|
static void mod_gnutls_context_release(mod_context *ctx) { |
|
if (!ctx) return; |
|
assert(g_atomic_int_get(&ctx->refcount) > 0); |
|
if (g_atomic_int_dec_and_test(&ctx->refcount)) { |
|
gnutls_priority_deinit(ctx->server_priority_beast); |
|
gnutls_priority_deinit(ctx->server_priority); |
|
gnutls_certificate_free_credentials(ctx->server_cert); |
|
gnutls_dh_params_deinit(ctx->dh_params); |
|
#ifdef HAVE_SESSION_TICKET |
|
/* wtf. why is there no function in gnutls for this... */ |
|
if (NULL != ctx->ticket_key.data) { |
|
gnutls_free(ctx->ticket_key.data); |
|
ctx->ticket_key.data = NULL; |
|
ctx->ticket_key.size = 0; |
|
} |
|
#endif |
|
li_ssl_session_db_free(ctx->session_db); |
|
ctx->session_db = NULL; |
|
|
|
#ifdef USE_SNI |
|
if (NULL != ctx->sni_db) { |
|
li_fetch_database_release(ctx->sni_db); |
|
ctx->sni_db = NULL; |
|
} |
|
if (NULL != ctx->sni_backend_db) { |
|
li_fetch_database_release(ctx->sni_backend_db); |
|
ctx->sni_backend_db = NULL; |
|
} |
|
if (NULL != ctx->sni_fallback_cert) { |
|
gnutls_certificate_free_credentials(ctx->sni_fallback_cert); |
|
ctx->sni_fallback_cert = NULL; |
|
} |
|
#endif |
|
|
|
|
|
g_slice_free(mod_context, ctx); |
|
} |
|
} |
|
|
|
static void mod_gnutls_context_acquire(mod_context *ctx) { |
|
assert(g_atomic_int_get(&ctx->refcount) > 0); |
|
g_atomic_int_inc(&ctx->refcount); |
|
} |
|
|
|
|
|
static mod_context *mod_gnutls_context_new(liServer *srv) { |
|
mod_context *ctx = g_slice_new0(mod_context); |
|
int r; |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_allocate_credentials(&ctx->server_cert))) { |
|
ERROR(srv, "gnutls_certificate_allocate_credentials failed(%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error0; |
|
} |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&ctx->server_priority, "NORMAL", NULL))) { |
|
ERROR(srv, "gnutls_priority_init failed(%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error1; |
|
} |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&ctx->server_priority_beast, "NORMAL:-CIPHER-ALL:+ARCFOUR-128", NULL))) { |
|
ERROR(srv, "gnutls_priority_init failed(%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error2; |
|
} |
|
|
|
#ifdef HAVE_SESSION_TICKET |
|
if (GNUTLS_E_SUCCESS != (r = gnutls_session_ticket_key_generate(&ctx->ticket_key))) { |
|
ERROR(srv, "gnutls_session_ticket_key_generate failed(%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error3; |
|
} |
|
#endif |
|
|
|
ctx->refcount = 1; |
|
ctx->protect_against_beast = 1; |
|
|
|
return ctx; |
|
|
|
error3: |
|
gnutls_priority_deinit(ctx->server_priority_beast); |
|
|
|
error2: |
|
gnutls_priority_deinit(ctx->server_priority); |
|
|
|
error1: |
|
gnutls_certificate_free_credentials(ctx->server_cert); |
|
|
|
error0: |
|
g_slice_free(mod_context, ctx); |
|
return NULL; |
|
} |
|
|
|
static void tcp_io_cb(liIOStream *stream, liIOStreamEvent event) { |
|
mod_connection_ctx *conctx = stream->data; |
|
assert(NULL == conctx->sock_stream || conctx->sock_stream == stream); |
|
|
|
if (LI_IOSTREAM_DESTROY == event) { |
|
li_stream_simple_socket_close(stream, TRUE); /* kill it, ssl sent an close alert message */ |
|
} |
|
|
|
li_connection_simple_tcp(&conctx->con, stream, &conctx->simple_socket_data, event); |
|
|
|
if (NULL != conctx->con && conctx->con->out_has_all_data |
|
&& (NULL == stream->stream_out.out || 0 == stream->stream_out.out->length) |
|
&& li_streams_empty(conctx->con->con_sock.raw_out, NULL)) { |
|
li_stream_simple_socket_flush(stream); |
|
li_connection_request_done(conctx->con); |
|
} |
|
|
|
switch (event) { |
|
case LI_IOSTREAM_DESTROY: |
|
assert(NULL == conctx->sock_stream); |
|
assert(NULL == conctx->tls_filter); |
|
assert(NULL == conctx->con); |
|
stream->data = NULL; |
|
g_slice_free(mod_connection_ctx, conctx); |
|
return; |
|
default: |
|
break; |
|
} |
|
} |
|
|
|
static void handshake_cb(liGnuTLSFilter *f, gpointer data, liStream *plain_source, liStream *plain_drain) { |
|
mod_connection_ctx *conctx = data; |
|
liConnection *con = conctx->con; |
|
UNUSED(f); |
|
|
|
if (NULL != con) { |
|
li_stream_connect(plain_source, con->con_sock.raw_in); |
|
li_stream_connect(con->con_sock.raw_out, plain_drain); |
|
} else { |
|
li_stream_reset(plain_source); |
|
li_stream_reset(plain_drain); |
|
} |
|
} |
|
|
|
static void close_cb(liGnuTLSFilter *f, gpointer data) { |
|
mod_connection_ctx *conctx = data; |
|
liConnection *con = conctx->con; |
|
assert(conctx->tls_filter == f); |
|
|
|
conctx->tls_filter = NULL; |
|
li_gnutls_filter_free(f); |
|
gnutls_deinit(conctx->session); |
|
|
|
if (NULL != conctx->ctx) { |
|
mod_gnutls_context_release(conctx->ctx); |
|
conctx->ctx = NULL; |
|
} |
|
|
|
if (NULL != conctx->con) { |
|
liStream *raw_out = con->con_sock.raw_out, *raw_in = con->con_sock.raw_in; |
|
assert(con->con_sock.data == conctx); |
|
conctx->con = NULL; |
|
con->con_sock.data = NULL; |
|
con->con_sock.callbacks = NULL; |
|
li_stream_acquire(raw_in); |
|
li_stream_reset(raw_out); |
|
li_stream_reset(raw_in); |
|
li_stream_release(raw_in); |
|
} |
|
|
|
#ifdef USE_SNI |
|
if (NULL != conctx->sni_db_wait) { |
|
li_fetch_cancel(&conctx->sni_db_wait); |
|
} |
|
if (NULL != conctx->sni_entry) { |
|
li_fetch_entry_release(conctx->sni_entry); |
|
conctx->sni_entry = NULL; |
|
} |
|
#endif |
|
|
|
if (NULL != conctx->client_hello_stream) { |
|
li_ssl_client_hello_stream_ready(conctx->client_hello_stream); |
|
li_stream_release(conctx->client_hello_stream); |
|
conctx->client_hello_stream = NULL; |
|
} |
|
|
|
#ifdef USE_SNI |
|
if (NULL != conctx->sni_jobref) { |
|
li_job_ref_release(conctx->sni_jobref); |
|
conctx->sni_jobref = NULL; |
|
} |
|
li_job_clear(&conctx->sni_job); |
|
|
|
if (NULL != conctx->sni_server_name) { |
|
g_string_free(conctx->sni_server_name, TRUE); |
|
conctx->sni_server_name = NULL; |
|
} |
|
#endif |
|
|
|
assert(NULL != conctx->sock_stream); |
|
li_iostream_safe_release(&conctx->sock_stream); |
|
} |
|
|
|
static int session_db_store_cb(void *_sdb, gnutls_datum_t key, gnutls_datum_t data) { |
|
liSSLSessionDB *sdb = _sdb; |
|
li_ssl_session_db_store(sdb, key.data, key.size, data.data, data.size); |
|
return 0; |
|
} |
|
static int session_db_remove_cb(void *_sdb, gnutls_datum_t key) { |
|
liSSLSessionDB *sdb = _sdb; |
|
li_ssl_session_db_remove(sdb, key.data, key.size); |
|
return 0; |
|
} |
|
static gnutls_datum_t session_db_retrieve_cb(void *_sdb, gnutls_datum_t key) { |
|
liSSLSessionDB *sdb = _sdb; |
|
liSSLSessionDBData *data = li_ssl_session_db_lookup(sdb, key.data, key.size); |
|
gnutls_datum_t result = { NULL, 0 }; |
|
if (NULL != data) { |
|
result.size = data->size; |
|
result.data = gnutls_malloc(result.size); |
|
memcpy(result.data, data->data, result.size); |
|
li_ssl_session_db_data_release(data); |
|
} |
|
return result; |
|
} |
|
|
|
static const liGnuTLSFilterCallbacks filter_callbacks = { |
|
handshake_cb, |
|
close_cb, |
|
NULL |
|
}; |
|
|
|
static void gnutls_tcp_finished(liConnection *con, gboolean aborted) { |
|
mod_connection_ctx *conctx = con->con_sock.data; |
|
UNUSED(aborted); |
|
|
|
con->info.is_ssl = FALSE; |
|
con->con_sock.callbacks = NULL; |
|
|
|
if (NULL != conctx) { |
|
assert(con == conctx->con); |
|
close_cb(conctx->tls_filter, conctx); |
|
assert(NULL == con->con_sock.data); |
|
} |
|
|
|
{ |
|
liStream *raw_out = con->con_sock.raw_out, *raw_in = con->con_sock.raw_in; |
|
con->con_sock.raw_out = con->con_sock.raw_in = NULL; |
|
if (NULL != raw_out) { li_stream_reset(raw_out); li_stream_release(raw_out); } |
|
if (NULL != raw_in) { li_stream_reset(raw_in); li_stream_release(raw_in); } |
|
} |
|
} |
|
|
|
static liThrottleState* gnutls_tcp_throttle_out(liConnection *con) { |
|
mod_connection_ctx *conctx = con->con_sock.data; |
|
if (NULL == conctx) return NULL; |
|
if (NULL == conctx->sock_stream->throttle_out) conctx->sock_stream->throttle_out = li_throttle_new(); |
|
return conctx->sock_stream->throttle_out; |
|
} |
|
|
|
static liThrottleState* gnutls_tcp_throttle_in(liConnection *con) { |
|
mod_connection_ctx *conctx = con->con_sock.data; |
|
if (NULL == conctx) return NULL; |
|
if (NULL == conctx->sock_stream->throttle_in) conctx->sock_stream->throttle_in = li_throttle_new(); |
|
return conctx->sock_stream->throttle_in; |
|
} |
|
|
|
static const liConnectionSocketCallbacks gnutls_tcp_cbs = { |
|
gnutls_tcp_finished, |
|
gnutls_tcp_throttle_out, |
|
gnutls_tcp_throttle_in |
|
}; |
|
|
|
#ifdef USE_SNI |
|
static void sni_job_cb(liJob *job) { |
|
mod_connection_ctx *conctx = LI_CONTAINER_OF(job, mod_connection_ctx, sni_job); |
|
|
|
assert(NULL != conctx->client_hello_stream); |
|
|
|
conctx->sni_entry = li_fetch_get(conctx->ctx->sni_db, conctx->sni_server_name, conctx->sni_jobref, &conctx->sni_db_wait); |
|
if (conctx->sni_entry != NULL) { |
|
gnutls_certificate_credentials_t creds = conctx->sni_entry->data; |
|
if (NULL != creds) { |
|
gnutls_credentials_set(conctx->session, GNUTLS_CRD_CERTIFICATE, creds); |
|
} else if (NULL != conctx->ctx->sni_fallback_cert) { |
|
gnutls_credentials_set(conctx->session, GNUTLS_CRD_CERTIFICATE, conctx->ctx->sni_fallback_cert); |
|
} |
|
li_ssl_client_hello_stream_ready(conctx->client_hello_stream); |
|
} |
|
} |
|
#endif |
|
|
|
static void gnutls_client_hello_cb(gpointer data, gboolean success, GString *server_name, guint16 protocol) { |
|
mod_connection_ctx *conctx = data; |
|
|
|
if (conctx->ctx->protect_against_beast) { |
|
if (!success || protocol <= 0x0301) { /* SSL3: 0x0300, TLS1.0: 0x0301 */ |
|
gnutls_priority_set(conctx->session, conctx->ctx->server_priority_beast); |
|
} |
|
} |
|
|
|
#ifdef USE_SNI |
|
if (success && NULL != server_name && NULL != conctx->ctx->sni_db && server_name->len > 0) { |
|
conctx->sni_server_name = g_string_new_len(GSTR_LEN(server_name)); |
|
sni_job_cb(&conctx->sni_job); |
|
return; |
|
} |
|
#else |
|
UNUSED(server_name); |
|
#endif |
|
|
|
li_ssl_client_hello_stream_ready(conctx->client_hello_stream); |
|
} |
|
|
|
static gboolean mod_gnutls_con_new(liConnection *con, int fd) { |
|
liEventLoop *loop = &con->wrk->loop; |
|
liServer *srv = con->srv; |
|
mod_context *ctx = con->srv_sock->data; |
|
mod_connection_ctx *conctx; |
|
gnutls_session_t session; |
|
int r; |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_init(&session, GNUTLS_SERVER))) { |
|
ERROR(srv, "gnutls_init (%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
return FALSE; |
|
} |
|
|
|
mod_gnutls_context_acquire(ctx); |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_priority_set(session, ctx->server_priority))) { |
|
ERROR(srv, "gnutls_priority_set (%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto fail; |
|
} |
|
if (GNUTLS_E_SUCCESS != (r = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, ctx->server_cert))) { |
|
ERROR(srv, "gnutls_credentials_set (%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto fail; |
|
} |
|
|
|
if (NULL != ctx->session_db) { |
|
gnutls_db_set_ptr(session, ctx->session_db); |
|
gnutls_db_set_remove_function(session, session_db_remove_cb); |
|
gnutls_db_set_retrieve_function(session, session_db_retrieve_cb); |
|
gnutls_db_set_store_function(session, session_db_store_cb); |
|
} |
|
|
|
#ifdef HAVE_SESSION_TICKET |
|
if (GNUTLS_E_SUCCESS != (r = gnutls_session_ticket_enable_server(session, &ctx->ticket_key))) { |
|
ERROR(srv, "gnutls_session_ticket_enable_server (%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto fail; |
|
} |
|
#endif |
|
|
|
conctx = g_slice_new0(mod_connection_ctx); |
|
conctx->session = session; |
|
conctx->sock_stream = li_iostream_new(con->wrk, fd, tcp_io_cb, conctx); |
|
|
|
conctx->client_hello_stream = li_ssl_client_hello_stream(&con->wrk->loop, gnutls_client_hello_cb, conctx); |
|
#ifdef USE_SNI |
|
li_job_init(&conctx->sni_job, sni_job_cb); |
|
conctx->sni_jobref = li_job_ref(&con->wrk->loop.jobqueue, &conctx->sni_job); |
|
#endif |
|
|
|
li_stream_connect(&conctx->sock_stream->stream_in, conctx->client_hello_stream); |
|
|
|
conctx->tls_filter = li_gnutls_filter_new(srv, con->wrk, &filter_callbacks, conctx, conctx->session, |
|
conctx->client_hello_stream, &conctx->sock_stream->stream_out); |
|
|
|
conctx->con = con; |
|
conctx->ctx = ctx; |
|
|
|
con->con_sock.data = conctx; |
|
con->con_sock.callbacks = &gnutls_tcp_cbs; |
|
con->con_sock.raw_out = li_stream_plug_new(loop); |
|
con->con_sock.raw_in = li_stream_plug_new(loop); |
|
con->info.is_ssl = TRUE; |
|
|
|
return TRUE; |
|
|
|
fail: |
|
gnutls_deinit(session); |
|
mod_gnutls_context_release(ctx); |
|
|
|
return FALSE; |
|
} |
|
|
|
|
|
static void mod_gnutls_sock_release(liServerSocket *srv_sock) { |
|
mod_context *ctx = srv_sock->data; |
|
|
|
if (!ctx) return; |
|
|
|
mod_gnutls_context_release(ctx); |
|
} |
|
|
|
static void gnutls_setup_listen_cb(liServer *srv, int fd, gpointer data) { |
|
mod_context *ctx = data; |
|
liServerSocket *srv_sock; |
|
UNUSED(data); |
|
|
|
if (-1 == fd) { |
|
mod_gnutls_context_release(ctx); |
|
return; |
|
} |
|
|
|
srv_sock = li_server_listen(srv, fd); |
|
|
|
srv_sock->data = ctx; /* transfer ownership, no refcount change */ |
|
|
|
srv_sock->new_cb = mod_gnutls_con_new; |
|
srv_sock->release_cb = mod_gnutls_sock_release; |
|
} |
|
|
|
static gboolean gnutls_setup(liServer *srv, liPlugin* p, liValue *val, gpointer userdata) { |
|
mod_context *ctx; |
|
int r; |
|
guint i; |
|
|
|
/* setup defaults */ |
|
gboolean have_listen_parameter = FALSE; |
|
gboolean have_pemfile_parameter = FALSE; |
|
gboolean have_protect_beast_parameter = FALSE; |
|
gboolean have_session_db_size_parameter = FALSE; |
|
const char |
|
*priority = NULL, *dh_params_file = NULL |
|
#ifdef USE_SNI |
|
,*sni_backend = NULL, *sni_fallback_pemfile = NULL |
|
#endif |
|
; |
|
gboolean |
|
protect_against_beast = TRUE; |
|
gint64 session_db_size = 256; |
|
|
|
UNUSED(p); UNUSED(userdata); |
|
|
|
if (NULL == (val = li_value_to_key_value_list(val))) { |
|
ERROR(srv, "%s", "gnutls expects a hash/key-value list as parameter"); |
|
return FALSE; |
|
} |
|
|
|
for (i = 0; i < val->data.list->len; ++i) { |
|
liValue *entryKey = g_array_index(g_array_index(val->data.list, liValue*, i)->data.list, liValue*, 0); |
|
liValue *entryValue = g_array_index(g_array_index(val->data.list, liValue*, i)->data.list, liValue*, 1); |
|
GString *entryKeyStr; |
|
|
|
if (entryKey->type == LI_VALUE_NONE) { |
|
ERROR(srv, "%s", "gnutls doesn't take null keys"); |
|
return FALSE; |
|
} |
|
entryKeyStr = entryKey->data.string; /* keys are either NONE or STRING */ |
|
|
|
if (g_str_equal(entryKeyStr->str, "listen")) { |
|
if (entryValue->type != LI_VALUE_STRING) { |
|
ERROR(srv, "%s", "gnutls listen expects a string as parameter"); |
|
return FALSE; |
|
} |
|
have_listen_parameter = TRUE; |
|
} else if (g_str_equal(entryKeyStr->str, "pemfile")) { |
|
if (entryValue->type != LI_VALUE_STRING) { |
|
ERROR(srv, "%s", "gnutls pemfile expects a string as parameter"); |
|
return FALSE; |
|
} |
|
have_pemfile_parameter = TRUE; |
|
} else if (g_str_equal(entryKeyStr->str, "dh-params")) { |
|
if (entryValue->type != LI_VALUE_STRING) { |
|
ERROR(srv, "%s", "gnutls dh-params expects a string as parameter"); |
|
return FALSE; |
|
} |
|
if (NULL != dh_params_file) { |
|
ERROR(srv, "gnutls unexpected duplicate parameter %s", entryKeyStr->str); |
|
return FALSE; |
|
} |
|
dh_params_file = entryValue->data.string->str; |
|
} else if (g_str_equal(entryKeyStr->str, "priority")) { |
|
if (entryValue->type != LI_VALUE_STRING) { |
|
ERROR(srv, "%s", "gnutls priority expects a string as parameter"); |
|
return FALSE; |
|
} |
|
if (NULL != priority) { |
|
ERROR(srv, "gnutls unexpected duplicate parameter %s", entryKeyStr->str); |
|
return FALSE; |
|
} |
|
priority = entryValue->data.string->str; |
|
} else if (g_str_equal(entryKeyStr->str, "protect-against-beast")) { |
|
if (entryValue->type != LI_VALUE_BOOLEAN) { |
|
ERROR(srv, "%s", "gnutls protect-against-beast expects a boolean as parameter"); |
|
return FALSE; |
|
} |
|
if (have_protect_beast_parameter) { |
|
ERROR(srv, "gnutls unexpected duplicate parameter %s", entryKeyStr->str); |
|
return FALSE; |
|
} |
|
have_protect_beast_parameter = TRUE; |
|
protect_against_beast = entryValue->data.boolean; |
|
} else if (g_str_equal(entryKeyStr->str, "session-db-size")) { |
|
if (entryValue->type != LI_VALUE_NUMBER) { |
|
ERROR(srv, "%s", "gnutls session-db-size expects an integer as parameter"); |
|
return FALSE; |
|
} |
|
if (have_session_db_size_parameter) { |
|
ERROR(srv, "gnutls unexpected duplicate parameter %s", entryKeyStr->str); |
|
return FALSE; |
|
} |
|
have_session_db_size_parameter = TRUE; |
|
session_db_size = entryValue->data.number; |
|
#ifdef USE_SNI |
|
} else if (g_str_equal(entryKeyStr->str, "sni-backend")) { |
|
if (entryValue->type != LI_VALUE_STRING) { |
|
ERROR(srv, "%s", "gnutls sni-backend expects a string as parameter"); |
|
return FALSE; |
|
} |
|
if (NULL != sni_backend) { |
|
ERROR(srv, "gnutls unexpected duplicate parameter %s", entryKeyStr->str); |
|
return FALSE; |
|
} |
|
sni_backend = entryValue->data.string->str; |
|
} else if (g_str_equal(entryKeyStr->str, "sni-fallback-pemfile")) { |
|
if (entryValue->type != LI_VALUE_STRING) { |
|
ERROR(srv, "%s", "gnutls sni-fallback-pemfile expects a string as parameter"); |
|
return FALSE; |
|
} |
|
if (NULL != sni_fallback_pemfile) { |
|
ERROR(srv, "gnutls unexpected duplicate parameter %s", entryKeyStr->str); |
|
return FALSE; |
|
} |
|
sni_fallback_pemfile = entryValue->data.string->str; |
|
#else |
|
} else if (g_str_equal(entryKeyStr->str, "sni-backend")) { |
|
ERROR(srv, "%s", "mod_gnutls was build without SNI support, invalid option sni-backend"); |
|
return FALSE; |
|
} else if (g_str_equal(entryKeyStr->str, "sni-fallback-pemfile")) { |
|
ERROR(srv, "%s", "mod_gnutls was build without SNI support, invalid option gnutls sni-fallback-pemfile"); |
|
return FALSE; |
|
#endif |
|
} else { |
|
ERROR(srv, "invalid parameter for gnutls: %s", entryKeyStr->str); |
|
return FALSE; |
|
} |
|
} |
|
|
|
if (!have_listen_parameter) { |
|
ERROR(srv, "%s", "gnutls needs a listen parameter"); |
|
return FALSE; |
|
} |
|
|
|
if (!have_pemfile_parameter) { |
|
ERROR(srv, "%s", "gnutls needs a pemfile"); |
|
return FALSE; |
|
} |
|
|
|
if (!(ctx = mod_gnutls_context_new(srv))) return FALSE; |
|
|
|
ctx->protect_against_beast = protect_against_beast; |
|
|
|
#ifdef USE_SNI |
|
if (NULL != sni_backend) { |
|
liFetchDatabase *backend = li_server_get_fetch_database(srv, sni_backend); |
|
if (NULL == backend) { |
|
ERROR(srv, "gnutls: no fetch backend with name '%s' registered", sni_backend); |
|
goto error_free_ctx; |
|
} |
|
ctx->sni_backend_db = backend; |
|
mod_gnutls_context_acquire(ctx); |
|
ctx->sni_db = li_fetch_database_new(&fetch_cert_callbacks, ctx, 64, 16); |
|
} |
|
|
|
if (NULL != sni_fallback_pemfile) { |
|
if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_allocate_credentials(&ctx->sni_fallback_cert))) { |
|
ERROR(srv, "gnutls_certificate_allocate_credentials failed(%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_set_x509_key_file(ctx->sni_fallback_cert, sni_fallback_pemfile, sni_fallback_pemfile, GNUTLS_X509_FMT_PEM))) { |
|
ERROR(srv, "gnutls_certificate_set_x509_key_file failed(certfile '%s', keyfile '%s', PEM) (%s): %s", |
|
sni_fallback_pemfile, sni_fallback_pemfile, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
} |
|
#endif |
|
|
|
for (i = 0; i < val->data.list->len; ++i) { |
|
liValue *entryKey = g_array_index(g_array_index(val->data.list, liValue*, i)->data.list, liValue*, 0); |
|
liValue *entryValue = g_array_index(g_array_index(val->data.list, liValue*, i)->data.list, liValue*, 1); |
|
GString *entryKeyStr; |
|
|
|
if (entryKey->type == LI_VALUE_NONE) continue; |
|
entryKeyStr = entryKey->data.string; /* keys are either NONE or STRING */ |
|
|
|
if (g_str_equal(entryKeyStr->str, "pemfile")) { |
|
const char *pemfile = entryValue->data.string->str; |
|
if (GNUTLS_E_SUCCESS != (r = gnutls_certificate_set_x509_key_file(ctx->server_cert, pemfile, pemfile, GNUTLS_X509_FMT_PEM))) { |
|
ERROR(srv, "gnutls_certificate_set_x509_key_file failed(certfile '%s', keyfile '%s', PEM) (%s): %s", |
|
pemfile, pemfile, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
} |
|
} |
|
|
|
if (session_db_size > 0) ctx->session_db = li_ssl_session_db_new(session_db_size); |
|
|
|
if (NULL != dh_params_file) { |
|
gchar *contents = NULL; |
|
gsize length = 0; |
|
GError *error = NULL; |
|
gnutls_datum_t pkcs3_params; |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_dh_params_init(&ctx->dh_params))) { |
|
ERROR(srv, "gnutls_dh_params_init failed (%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
|
|
if (!g_file_get_contents(dh_params_file, &contents, &length, &error)) { |
|
ERROR(srv, "Unable to read dh-params file '%s': %s", dh_params_file, error->message); |
|
g_error_free(error); |
|
goto error_free_ctx; |
|
} |
|
|
|
pkcs3_params.data = (unsigned char*) contents; |
|
pkcs3_params.size = length; |
|
|
|
r = gnutls_dh_params_import_pkcs3(ctx->dh_params, &pkcs3_params, GNUTLS_X509_FMT_PEM); |
|
g_free(contents); |
|
|
|
if (GNUTLS_E_SUCCESS != r) { |
|
ERROR(srv, "couldn't load dh parameters from file '%s' (%s): %s", |
|
dh_params_file, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
|
|
gnutls_certificate_set_dh_params(ctx->server_cert, ctx->dh_params); |
|
} else { |
|
if (GNUTLS_E_SUCCESS != (r = load_dh_params_4096(&ctx->dh_params))) { |
|
ERROR(srv, "couldn't load dh parameters(%s): %s", |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
|
|
gnutls_certificate_set_dh_params(ctx->server_cert, ctx->dh_params); |
|
} |
|
|
|
if (priority) { |
|
const char *errpos = NULL; |
|
gnutls_priority_t prio; |
|
GString *s = srv->main_worker->tmp_str; |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&prio, priority, &errpos))) { |
|
ERROR(srv, "gnutls_priority_init failed(priority '%s', error at '%s') (%s): %s", |
|
priority, errpos, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
|
|
gnutls_priority_deinit(ctx->server_priority); |
|
ctx->server_priority = prio; |
|
|
|
if (protect_against_beast) { |
|
g_string_assign(s, priority); |
|
g_string_append_len(s, CONST_STR_LEN(":-CIPHER-ALL:+ARCFOUR-128")); |
|
if (GNUTLS_E_SUCCESS != (r = gnutls_priority_init(&prio, s->str, &errpos))) { |
|
ERROR(srv, "gnutls_priority_init failed(priority '%s', error at '%s') (%s): %s", |
|
s->str, errpos, |
|
gnutls_strerror_name(r), gnutls_strerror(r)); |
|
goto error_free_ctx; |
|
} |
|
|
|
gnutls_priority_deinit(ctx->server_priority_beast); |
|
ctx->server_priority_beast = prio; |
|
} |
|
} |
|
|
|
for (i = 0; i < val->data.list->len; ++i) { |
|
liValue *entryKey = g_array_index(g_array_index(val->data.list, liValue*, i)->data.list, liValue*, 0); |
|
liValue *entryValue = g_array_index(g_array_index(val->data.list, liValue*, i)->data.list, liValue*, 1); |
|
GString *entryKeyStr; |
|
|
|
if (entryKey->type == LI_VALUE_NONE) continue; |
|
entryKeyStr = entryKey->data.string; /* keys are either NONE or STRING */ |
|
|
|
if (g_str_equal(entryKeyStr->str, "listen")) { |
|
mod_gnutls_context_acquire(ctx); |
|
li_angel_listen(srv, entryValue->data.string, gnutls_setup_listen_cb, ctx); |
|
} |
|
} |
|
|
|
mod_gnutls_context_release(ctx); |
|
|
|
return TRUE; |
|
|
|
error_free_ctx: |
|
if (ctx) { |
|
mod_gnutls_context_release(ctx); |
|
} |
|
|
|
return FALSE; |
|
} |
|
|
|
static const liPluginOption options[] = { |
|
{ NULL, 0, 0, NULL } |
|
}; |
|
|
|
static const liPluginAction actions[] = { |
|
{ NULL, NULL, NULL } |
|
}; |
|
|
|
static const liPluginSetup setups[] = { |
|
{ "gnutls", gnutls_setup, NULL }, |
|
|
|
{ NULL, NULL, NULL } |
|
}; |
|
|
|
|
|
static void plugin_init(liServer *srv, liPlugin *p, gpointer userdata) { |
|
UNUSED(srv); UNUSED(userdata); |
|
|
|
p->options = options; |
|
p->actions = actions; |
|
p->setups = setups; |
|
} |
|
|
|
gboolean mod_gnutls_init(liModules *mods, liModule *mod) { |
|
MODULE_VERSION_CHECK(mods); |
|
|
|
gnutls_global_init(); |
|
|
|
mod->config = li_plugin_register(mods->main, "mod_gnutls", plugin_init, NULL); |
|
|
|
return mod->config != NULL; |
|
} |
|
|
|
gboolean mod_gnutls_free(liModules *mods, liModule *mod) { |
|
if (mod->config) |
|
li_plugin_free(mods->main, mod->config); |
|
|
|
gnutls_global_deinit(); |
|
|
|
return TRUE; |
|
} |
|
|
|
/* |
|
|
|
# --sec-param=high in certtool 3.0.22 |
|
certtool --get-dh-params --bits=4096 | openssl dhparam -C |
|
|
|
-----BEGIN DH PARAMETERS----- |
|
MIICCAKCAgEA///////////JD9qiIWjCNMTGYouA3BzRKQJOCIpnzHQCC76mOxOb |
|
IlFKCHmONATd75UZs806QxswKwpt8l8UN0/hNW1tUcJF5IW1dmJefsb0TELppjft |
|
awv/XLb0Brft7jhr+1qJn6WunyQRfEsf5kkoZlHs5Fs9wgB8uKFjvwWY2kg2HFXT |
|
mmkWP6j9JM9fg2VdI9yjrZYcYvNWIIVSu57VKQdwlpZtZww1Tkq8mATxdGwIyhgh |
|
fDKQXkYuNs474553LBgOhgObJ4Oi7Aeij7XFXfBvTFLJ3ivL9pVYFxg5lUl86pVq |
|
5RXSJhiY+gUQFXKOWoqqxC2tMxcNBFB6M6hVIavfHLpk7PuFBFjb7wqK6nFXXQYM |
|
fbOXD4Wm4eTHq/WujNsJM9cejJTgSiVhnc7j0iYa0u5r8S/6BtmKCGTYdgJzPshq |
|
ZFIfKxgXeyAMu+EXV3phXWx3CYjAutlG4gjiT6B05asxQ9tb/OD9EI5LgtEgqSEI |
|
ARpyPBKnh+bXiHGaEL26WyaZwycYavTiPBqUaDS2FQvaJYPpyirUTOjbu8LbBN6O |
|
+S6O/BQfvsqmKHxZR05rwF2ZspZPoJDDoiM7oYZRW+ftH2EpcM7i16+4G912IXBI |
|
HNAGkSfVsFqpk7TqmI2P3cGG/7fckKbAj030Nck0BjGZ//////////8CAQU= |
|
-----END DH PARAMETERS----- |
|
|
|
*/ |
|
|
|
static int load_dh_params_4096(gnutls_dh_params_t *dh_params) { |
|
static const unsigned char dh4096_p[]={ |
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xC9,0x0F,0xDA,0xA2, |
|
0x21,0x68,0xC2,0x34,0xC4,0xC6,0x62,0x8B,0x80,0xDC,0x1C,0xD1, |
|
0x29,0x02,0x4E,0x08,0x8A,0x67,0xCC,0x74,0x02,0x0B,0xBE,0xA6, |
|
0x3B,0x13,0x9B,0x22,0x51,0x4A,0x08,0x79,0x8E,0x34,0x04,0xDD, |
|
0xEF,0x95,0x19,0xB3,0xCD,0x3A,0x43,0x1B,0x30,0x2B,0x0A,0x6D, |
|
0xF2,0x5F,0x14,0x37,0x4F,0xE1,0x35,0x6D,0x6D,0x51,0xC2,0x45, |
|
0xE4,0x85,0xB5,0x76,0x62,0x5E,0x7E,0xC6,0xF4,0x4C,0x42,0xE9, |
|
0xA6,0x37,0xED,0x6B,0x0B,0xFF,0x5C,0xB6,0xF4,0x06,0xB7,0xED, |
|
0xEE,0x38,0x6B,0xFB,0x5A,0x89,0x9F,0xA5,0xAE,0x9F,0x24,0x11, |
|
0x7C,0x4B,0x1F,0xE6,0x49,0x28,0x66,0x51,0xEC,0xE4,0x5B,0x3D, |
|
0xC2,0x00,0x7C,0xB8,0xA1,0x63,0xBF,0x05,0x98,0xDA,0x48,0x36, |
|
0x1C,0x55,0xD3,0x9A,0x69,0x16,0x3F,0xA8,0xFD,0x24,0xCF,0x5F, |
|
0x83,0x65,0x5D,0x23,0xDC,0xA3,0xAD,0x96,0x1C,0x62,0xF3,0x56, |
|
0x20,0x85,0x52,0xBB,0x9E,0xD5,0x29,0x07,0x70,0x96,0x96,0x6D, |
|
0x67,0x0C,0x35,0x4E,0x4A,0xBC,0x98,0x04,0xF1,0x74,0x6C,0x08, |
|
0xCA,0x18,0x21,0x7C,0x32,0x90,0x5E,0x46,0x2E,0x36,0xCE,0x3B, |
|
0xE3,0x9E,0x77,0x2C,0x18,0x0E,0x86,0x03,0x9B,0x27,0x83,0xA2, |
|
0xEC,0x07,0xA2,0x8F,0xB5,0xC5,0x5D,0xF0,0x6F,0x4C,0x52,0xC9, |
|
0xDE,0x2B,0xCB,0xF6,0x95,0x58,0x17,0x18,0x39,0x95,0x49,0x7C, |
|
0xEA,0x95,0x6A,0xE5,0x15,0xD2,0x26,0x18,0x98,0xFA,0x05,0x10, |
|
0x15,0x72,0x8E,0x5A,0x8A,0xAA,0xC4,0x2D,0xAD,0x33,0x17,0x0D, |
|
0x04,0x50,0x7A,0x33,0xA8,0x55,0x21,0xAB,0xDF,0x1C,0xBA,0x64, |
|
0xEC,0xFB,0x85,0x04,0x58,0xDB,0xEF,0x0A,0x8A,0xEA,0x71,0x57, |
|
0x5D,0x06,0x0C,0x7D,0xB3,0x97,0x0F,0x85,0xA6,0xE1,0xE4,0xC7, |
|
0xAB,0xF5,0xAE,0x8C,0xDB,0x09,0x33,0xD7,0x1E,0x8C,0x94,0xE0, |
|
0x4A,0x25,0x61,0x9D,0xCE,0xE3,0xD2,0x26,0x1A,0xD2,0xEE,0x6B, |
|
0xF1,0x2F,0xFA,0x06,0xD9,0x8A,0x08,0x64,0xD8,0x76,0x02,0x73, |
|
0x3E,0xC8,0x6A,0x64,0x52,0x1F,0x2B,0x18,0x17,0x7B,0x20,0x0C, |
|
0xBB,0xE1,0x17,0x57,0x7A,0x61,0x5D,0x6C,0x77,0x09,0x88,0xC0, |
|
0xBA,0xD9,0x46,0xE2,0x08,0xE2,0x4F,0xA0,0x74,0xE5,0xAB,0x31, |
|
0x43,0xDB,0x5B,0xFC,0xE0,0xFD,0x10,0x8E,0x4B,0x82,0xD1,0x20, |
|
0xA9,0x21,0x08,0x01,0x1A,0x72,0x3C,0x12,0xA7,0x87,0xE6,0xD7, |
|
0x88,0x71,0x9A,0x10,0xBD,0xBA,0x5B,0x26,0x99,0xC3,0x27,0x18, |
|
0x6A,0xF4,0xE2,0x3C,0x1A,0x94,0x68,0x34,0xB6,0x15,0x0B,0xDA, |
|
0x25,0x83,0xE9,0xCA,0x2A,0xD4,0x4C,0xE8,0xDB,0xBB,0xC2,0xDB, |
|
0x04,0xDE,0x8E,0xF9,0x2E,0x8E,0xFC,0x14,0x1F,0xBE,0xCA,0xA6, |
|
0x28,0x7C,0x59,0x47,0x4E,0x6B,0xC0,0x5D,0x99,0xB2,0x96,0x4F, |
|
0xA0,0x90,0xC3,0xA2,0x23,0x3B,0xA1,0x86,0x51,0x5B,0xE7,0xED, |
|
0x1F,0x61,0x29,0x70,0xCE,0xE2,0xD7,0xAF,0xB8,0x1B,0xDD,0x76, |
|
0x21,0x70,0x48,0x1C,0xD0,0x06,0x91,0x27,0xD5,0xB0,0x5A,0xA9, |
|
0x93,0xB4,0xEA,0x98,0x8D,0x8F,0xDD,0xC1,0x86,0xFF,0xB7,0xDC, |
|
0x90,0xA6,0xC0,0x8F,0x4D,0xF4,0x35,0xC9,0x34,0x06,0x31,0x99, |
|
0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF,0xFF, |
|
}; |
|
static const unsigned char dh4096_g[]={ |
|
0x05, |
|
}; |
|
static const gnutls_datum_t prime = { (unsigned char*) dh4096_p, sizeof(dh4096_p) }; |
|
static const gnutls_datum_t generator = { (unsigned char*) dh4096_g, sizeof(dh4096_g) }; |
|
int r; |
|
gnutls_dh_params_t params; |
|
|
|
if (GNUTLS_E_SUCCESS != (r = gnutls_dh_params_init(¶ms))) return r; |
|
if (GNUTLS_E_SUCCESS != (r = gnutls_dh_params_import_raw(params, &prime, &generator))) { |
|
gnutls_dh_params_deinit(params); |
|
return r; |
|
} |
|
*dh_params = params; |
|
return GNUTLS_E_SUCCESS; |
|
}
|
|
|