Skip to content
Snippets Groups Projects
Commit 99978a04 authored by Leif Johansson's avatar Leif Johansson
Browse files

Merge pull request #9 from fredrikt/master

TLS-PSK
parents 4a2fa85a 4ec86025
No related branches found
No related tags found
No related merge requests found
......@@ -6,8 +6,8 @@ endif(COMMAND cmake_policy)
project (pkcs11 C)
set(PKCS11_PROXY_SRCS gck-rpc-module.c gck-rpc-message.c gck-rpc-util.c egg-buffer.c)
set(PKCS11_DAEMON_SRCS egg-buffer.c gck-rpc-daemon-standalone.c gck-rpc-dispatch.c gck-rpc-message.c gck-rpc-util.c syscall-reporter.c syscall-names.h)
set(PKCS11_PROXY_SRCS gck-rpc-module.c gck-rpc-message.c gck-rpc-util.c egg-buffer.c gck-rpc-tls-psk.c)
set(PKCS11_DAEMON_SRCS egg-buffer.c gck-rpc-daemon-standalone.c gck-rpc-dispatch.c gck-rpc-message.c gck-rpc-util.c syscall-reporter.c syscall-names.h gck-rpc-tls-psk.c)
add_definitions(-Wall)
add_library(pkcs11-proxy SHARED ${PKCS11_PROXY_SRCS})
......@@ -33,8 +33,8 @@ if (WIN32)
target_link_libraries (pkcs11-proxy ws2_32)
endif (WIN32)
target_link_libraries (pkcs11-proxy pthread)
target_link_libraries (pkcs11-daemon dl pthread)
target_link_libraries (pkcs11-proxy pthread ssl)
target_link_libraries (pkcs11-daemon dl pthread ssl)
install_targets (/lib pkcs11-proxy)
install_targets (/bin pkcs11-daemon)
......
......@@ -6,5 +6,10 @@ This fork has the following additional features:
- support for running in "inetd mode", useful for calling directly from stunnel
- seccomp syscall filtering (only tested in inetd-mode)
- getaddrinfo support for IPv6, fallback and DNS resolution
- TLS-PSK support to optionally encrypt communication
Plus a number of important bug fixes. This version passes the SoftHSM test
suite.
An ubuntu PPA that tracks this version is ppa:leifj
......@@ -5,9 +5,10 @@ dependencies and other features.
The proxy tunnels PKCS11-requests over the network. One possible use
is to store cryptograhic information on a seperate server. This way
the crypto it can be isolated from the rest of the system. Beware:
the connection is not encrypted and can easily be sniffed. You should
use a secure communication-channel, for example stunnel.
the crypto can be isolated from the rest of the system.
Example
=======
Here is an example of using pkcs11-proxy together with SoftHSM (from the
OpenDNSSEC project). The benefit of this setup is that no extra hardware
......@@ -30,3 +31,52 @@ slots: Slot 0 SoftHSM
token label: test token manuf: SoftHSM token model: SoftHSM
token flags: rng, login required, PIN initialized, token initialized,
other flags=0x40 serial num : 1
IPv6 and DNS
============
The PKCS11_DAEMON_SOCKET and PKCS11_PROXY_SOCKET environment variables can
have both hostnames and IPv6 addresses in them. getaddrinfo(3) is used to
resolve any DNS name.
$ PKCS11_DAEMON_SOCKET="tcp://server.example.com:2345" ...
If `server.example.com' resolves to more than one IP address (such as one
IPv4 and one IPv6 address), these will be tried sequentially. Currently,
no attempt is made to speed up connection establishment using Happy Eyeballs
(RFC 6555) or similar, so timeouts in case of unreachable addresses could
be expected to be quite problematic.
IPv6 addresses should be enclosed by square brackets.
$ PKCS11_DAEMON_SOCKET="tcp://[::1]:2345" ...
Encryption
==========
This version supports encrypting the communication between the client and
proxy using OpenSSL TLS-PSK (pre-shared key). The PSK is read from a file
that usees the GnuTLS psktool format. This format includes PSK identity
as well as key.
Currently, there is no way to specify the identity to use on the client
side (client tells server what identity should be used), and the first
identity found in the PSK file is used. The server side will correctly
look up the identity requested by the client in it's PSK file, so it is
possible to have one unique PSK identity and key per PKCS11 client, and
have all the identitys and keys in the PSK file for the PKCS11 daemon.
$ cat test.psk
test:e9622c85018998993fcc16f5ce9c15e9
$ PKCS11_PROXY_TLS_PSK_FILE="test.psk" \
PKCS11_DAEMON_SOCKET="tls://server.example.com:2345" \
pkcs11-daemon /usr/lib/libsofthsm.so
$ PKCS11_PROXY_TLS_PSK_FILE="test.psk" \
PKCS11_PROXY_SOCKET="tls://server.example.com:2345" \
pkcs11-tool --module=/usr/lib/libpkcs11-proxy.so -L Available
slots: Slot 0 SoftHSM
token label: test token manuf: SoftHSM token model: SoftHSM
token flags: rng, login required, PIN initialized, token initialized,
other flags=0x40 serial num : 1
......@@ -6,6 +6,8 @@
# define PKCS11PROXY_LISTEN_BACKLOG 128
# define PKCS11PROXY_MAX_SESSION_COUNT 256
# define PKCS11PROXY_TLS_PSK_CIPHERS "PSK-AES128-CBC-SHA:PSK-AES256-CBC-SHA";
#ifdef __MINGW32__
# include <stdint.h>
......
......@@ -26,6 +26,7 @@
#include "pkcs11/pkcs11.h"
#include "gck-rpc-layer.h"
#include "gck-rpc-tls-psk.h"
#include <stdio.h>
#include <errno.h>
......@@ -166,12 +167,12 @@ int main(int argc, char *argv[])
CK_C_GetFunctionList func_get_list;
CK_FUNCTION_LIST_PTR funcs;
void *module;
const char *path;
const char *path, *tls_psk_keyfile;
fd_set read_fds;
int sock, ret;
CK_RV rv;
CK_C_INITIALIZE_ARGS init_args;
GckRpcTlsPskState *tls;
if (install_syscall_reporter())
return 1;
......@@ -229,6 +230,27 @@ int main(int argc, char *argv[])
if (!path)
path = SOCKET_PATH;
/* Initialize TLS, if appropriate */
tls = NULL;
if (! strncmp("tls://", path, 6)) {
tls_psk_keyfile = getenv("PKCS11_PROXY_TLS_PSK_FILE");
if (! tls_psk_keyfile || ! tls_psk_keyfile[0]) {
fprintf(stderr, "key file must be specified for tls:// socket.\n");
exit(1);
}
tls = calloc(1, sizeof(GckRpcTlsPskState));
if (tls == NULL) {
fprintf(stderr, "can't allocate memory for TLS-PSK");
exit(1);
}
if (! gck_rpc_init_tls_psk(tls, tls_psk_keyfile, NULL, GCK_RPC_TLS_PSK_SERVER)) {
fprintf(stderr, "TLS-PSK initialization failed");
exit(1);
}
}
if (strcmp(path,"-") == 0) {
gck_rpc_layer_inetd(funcs);
} else {
......@@ -254,7 +276,7 @@ int main(int argc, char *argv[])
}
if (FD_ISSET(sock, &read_fds))
gck_rpc_layer_accept();
gck_rpc_layer_accept(tls);
}
gck_rpc_layer_uninitialize();
......@@ -267,5 +289,8 @@ int main(int argc, char *argv[])
dlclose(module);
if (tls)
gck_rpc_close_tls(tls);
return 0;
}
......@@ -25,6 +25,7 @@
#include "gck-rpc-layer.h"
#include "gck-rpc-private.h"
#include "gck-rpc-tls-psk.h"
#include "pkcs11/pkcs11.h"
#include "pkcs11/pkcs11g.h"
......@@ -71,14 +72,15 @@ typedef struct _CallState {
uint64_t appid;
int call;
int sock;
int (*read)(int, unsigned char *,size_t);
int (*write)(int, unsigned char *,size_t);
int (*read)(void *cs, unsigned char *,size_t);
int (*write)(void *cs, unsigned char *,size_t);
struct sockaddr_storage addr;
socklen_t addrlen;
/* XXX Maybe sessions should be a linked list instead, to remove the hard
* upper limit and reduce typical memory use.
*/
SessionState sessions[PKCS11PROXY_MAX_SESSION_COUNT];
GckRpcTlsPskState *tls;
} CallState;
typedef struct _DispatchState {
......@@ -2137,17 +2139,20 @@ static int dispatch_call(CallState * cs)
return 1;
}
static int read_all(int sock, unsigned char *data, size_t len)
static int read_all(CallState *cs, void *data, size_t len)
{
int r;
assert(sock >= 0);
assert(cs->sock >= 0);
assert(data);
assert(len > 0);
while (len > 0) {
r = recv(sock, (void *)data, len, 0);
if (cs->tls)
r = gck_rpc_tls_read_all(cs->tls, data, len);
else
r = recv(cs->sock, data, len, 0);
if (r == 0) {
/* Connection was closed on client */
......@@ -2166,17 +2171,20 @@ static int read_all(int sock, unsigned char *data, size_t len)
return 1;
}
static int write_all(int sock, unsigned char *data, size_t len)
static int write_all(CallState *cs, void *data, size_t len)
{
int r;
assert(sock >= 0);
assert(cs->sock >= 0);
assert(data);
assert(len > 0);
while (len > 0) {
r = send(sock, (void *)data, len, MSG_NOSIGNAL);
if (cs->tls)
r = gck_rpc_tls_write_all(cs->tls, (void *) data, len);
else
r = send(cs->sock, data, len, MSG_NOSIGNAL);
if (r == -1) {
if (errno == EPIPE) {
......@@ -2212,8 +2220,16 @@ static void run_dispatch_loop(CallState *cs)
hoststr[0] = portstr[0] = '\0';
}
/* Enable TLS for this socket */
if (cs->tls) {
if (! gck_rpc_start_tls(cs->tls, cs->sock)) {
gck_rpc_warn("Can't enable TLS");
return ;
}
}
/* The client application */
if (!cs->read(cs->sock, (unsigned char *)&cs->appid, sizeof (cs->appid))) {
if (! cs->read(cs, (void *)&cs->appid, sizeof (cs->appid))) {
gck_rpc_warn("Can't read appid\n");
return ;
}
......@@ -2233,7 +2249,7 @@ static void run_dispatch_loop(CallState *cs)
call_reset(cs);
/* Read the number of bytes ... */
if (!cs->read(cs->sock, buf, 4))
if (! cs->read(cs, buf, 4))
break;
/* Calculate the number of bytes */
......@@ -2252,7 +2268,7 @@ static void run_dispatch_loop(CallState *cs)
}
/* ... and read/parse in the actual message */
if (!cs->read(cs->sock, cs->req->buffer.buf, len))
if (!cs->read(cs, cs->req->buffer.buf, len))
break;
egg_buffer_add_empty(&cs->req->buffer, len);
......@@ -2266,8 +2282,8 @@ static void run_dispatch_loop(CallState *cs)
/* .. send back response length, and then response data */
egg_buffer_encode_uint32(buf, cs->resp->buffer.len);
if (!cs->write(cs->sock, buf, 4) ||
!cs->write(cs->sock, cs->resp->buffer.buf, cs->resp->buffer.len))
if (!cs->write(cs, buf, 4) ||
!cs->write(cs, cs->resp->buffer.buf, cs->resp->buffer.len))
break;
}
......@@ -2299,7 +2315,7 @@ static int pkcs11_socket = -1;
/* The unix socket path, that we listen on */
static char pkcs11_socket_path[MAXPATHLEN] = { 0, };
void gck_rpc_layer_accept(void)
void gck_rpc_layer_accept(GckRpcTlsPskState *tls)
{
struct sockaddr_storage addr;
DispatchState *ds, **here;
......@@ -2342,6 +2358,7 @@ void gck_rpc_layer_accept(void)
ds->cs.write = &write_all;
ds->cs.addr = addr;
ds->cs.addrlen = addrlen;
ds->cs.tls = tls;
error = pthread_create(&ds->thread, NULL,
run_dispatch_thread, &(ds->cs));
......@@ -2357,14 +2374,26 @@ void gck_rpc_layer_accept(void)
pthread_mutex_unlock(&pkcs11_dispatchers_mutex);
}
static int _inetd_read(CallState *cs, void *data, size_t len)
{
assert(cs->sock >= 0);
return read(cs->sock, data, len);
}
static int _inetd_write(CallState *cs, void *data, size_t len)
{
assert(cs->sock >= 0);
return write(cs->sock, data, len);
}
void gck_rpc_layer_inetd(CK_FUNCTION_LIST_PTR module)
{
CallState cs;
memset(&cs, 0, sizeof(cs));
cs.sock = STDIN_FILENO;
cs.read = &read;
cs.write = &write;
cs.read = &_inetd_read;
cs.write = &_inetd_write;
pkcs11_module = module;
......@@ -2496,7 +2525,8 @@ int gck_rpc_layer_initialize(const char *prefix, CK_FUNCTION_LIST_PTR module)
}
#endif
if (!strncmp("tcp://", prefix, 6)) {
if (!strncmp("tcp://", prefix, 6) ||
!strncmp("tls://", prefix, 6)) {
/*
* TCP socket
*/
......
......@@ -3,6 +3,8 @@
#include "pkcs11/pkcs11.h"
#include "gck-rpc-tls-psk.h"
/* ------------------------------------------------------------------
* DISPATCHER
*/
......@@ -14,7 +16,7 @@ int gck_rpc_layer_initialize(const char *prefix, CK_FUNCTION_LIST_PTR funcs);
void gck_rpc_layer_uninitialize(void);
/* Accept a new connection. Should be called when above fd has read */
void gck_rpc_layer_accept(void);
void gck_rpc_layer_accept(GckRpcTlsPskState *tls);
/* Run a single connection off of STDIN - call from inetd or stunnel */
void gck_rpc_layer_inetd(CK_FUNCTION_LIST_PTR funcs);
......
......@@ -25,6 +25,7 @@
#include "gck-rpc-layer.h"
#include "gck-rpc-private.h"
#include "gck-rpc-tls-psk.h"
#include "pkcs11/pkcs11.h"
......@@ -67,6 +68,8 @@ static uint64_t pkcs11_app_id = 0;
/* The socket to connect to */
static char pkcs11_socket_path[MAXPATHLEN] = { 0, };
/* The TLS-PSK keyfile name */
static char tls_psk_key_filename[MAXPATHLEN] = { 0, };
/* The error used by us when parsing of rpc message fails */
#define PARSE_ERROR CKR_DEVICE_ERROR
......@@ -113,6 +116,9 @@ static void parse_argument(char *arg)
if (strcmp(arg, "socket") == 0)
snprintf(pkcs11_socket_path, sizeof(pkcs11_socket_path), "%s",
value);
else if (strcmp(arg, "tls_psk_file") == 0)
snprintf(tls_psk_key_filename, sizeof(tls_psk_key_filename), "%s",
value);
else
warning(("unrecognized argument: %s", arg));
}
......@@ -201,6 +207,7 @@ typedef struct _CallState {
GckRpcMessage *req; /* The current request */
GckRpcMessage *resp; /* The current response */
int call_status;
GckRpcTlsPskState *tls;
struct _CallState *next; /* For pooling of completed sockets */
} CallState;
......@@ -251,7 +258,10 @@ static CK_RV call_write(CallState * cs, unsigned char *data, size_t len)
return CKR_DEVICE_ERROR;
}
r = send(fd, (void *)data, len, 0);
if (cs->tls)
r = gck_rpc_tls_write_all(cs->tls, (void *) data, len);
else
r = send(fd, (void *) data, len, 0);
if (r == -1) {
if (errno == EPIPE) {
......@@ -290,7 +300,10 @@ static CK_RV call_read(CallState * cs, unsigned char *data, size_t len)
return CKR_DEVICE_ERROR;
}
r = recv(fd, (void *)data, len, 0);
if (cs->tls)
r = gck_rpc_tls_read_all(cs->tls, (void *) data, len);
else
r = recv(fd, (void *) data, len, 0);
if (r == 0) {
warning(("couldn't receive data: daemon closed connection"));
......@@ -409,7 +422,8 @@ static CK_RV call_connect(CallState * cs)
memset(&addr, 0, sizeof(addr));
if (!strncmp("tcp://", pkcs11_socket_path, 6)) {
if (! strncmp("tcp://", pkcs11_socket_path, 6) ||
! strncmp("tls://", pkcs11_socket_path, 6)) {
char *host, *port;
if (! gck_rpc_parse_host_port(pkcs11_socket_path + 6, &host, &port)) {
......@@ -424,6 +438,24 @@ static CK_RV call_connect(CallState * cs)
}
free(host);
if (! strncmp("tls://", pkcs11_socket_path, 6)) {
cs->tls = calloc(1, sizeof(GckRpcTlsPskState));
if (cs->tls == NULL) {
warning(("can't allocate memory for TLS-PSK"));
return CKR_HOST_MEMORY;
}
if (! gck_rpc_init_tls_psk(cs->tls, tls_psk_key_filename, NULL, GCK_RPC_TLS_PSK_CLIENT)) {
warning(("TLS-PSK initialization failed"));
return CKR_DEVICE_ERROR;
}
if (! gck_rpc_start_tls(cs->tls, sock)) {
gck_rpc_warn("failed starting TLS");
return CKR_DEVICE_ERROR;
}
}
} else {
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, pkcs11_socket_path,
......@@ -472,6 +504,9 @@ static void call_destroy(void *value)
gck_rpc_message_free(cs->req);
gck_rpc_message_free(cs->resp);
if (cs->tls)
gck_rpc_close_tls(cs->tls);
free(cs);
debug(("destroyed state"));
......@@ -1320,7 +1355,8 @@ static CK_RV rpc_C_Initialize(CK_VOID_PTR init_args)
if (pkcs11_socket_path[0] == 0) {
path = getenv("PKCS11_PROXY_SOCKET");
if (path && path[0]) {
if (!strncmp("tcp://", path, 6))
if ((! strncmp("tcp://", path, 6)) ||
(! strncmp("tls://", path, 6)))
snprintf(pkcs11_socket_path,
sizeof(pkcs11_socket_path), "%s",
path);
......@@ -1335,6 +1371,23 @@ static CK_RV rpc_C_Initialize(CK_VOID_PTR init_args)
}
}
/* If socket path indicates TLS, make sure tls_psk_key_filename is populated. */
if (! strncmp("tls://", pkcs11_socket_path, 6)) {
if (! tls_psk_key_filename[0]) {
path = getenv("PKCS11_PROXY_TLS_PSK_FILE");
if (path && path[0]) {
snprintf(tls_psk_key_filename, sizeof(tls_psk_key_filename),
"%s", path);
}
}
if (! tls_psk_key_filename[0]) {
warning(("can't handle tls:// path without a tls-psk file"));
ret = CKR_FUNCTION_NOT_SUPPORTED;
goto done;
}
}
srand(time(NULL) ^ pid);
pkcs11_app_id = (uint64_t) rand() << 32 | rand();
......
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
/* gck-rpc-tls-psk.c - TLS-PSK functionality to protect communication
Copyright (C) 2013, NORDUnet A/S
pkcs11-proxy 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.
pkcs11-proxy 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 the Gnome Library; see the file COPYING.LIB. If not,
write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Author: Fredrik Thulin <fredrik@thulin.net>
*/
#include "config.h"
#include "gck-rpc-private.h"
#include "gck-rpc-tls-psk.h"
#include <sys/param.h>
#include <assert.h>
/* TLS pre-shared key */
static char tls_psk_identity[128] = { 0, };
static char tls_psk_key_filename[MAXPATHLEN] = { 0, };
/* -----------------------------------------------------------------------------
* LOGGING and DEBUGGING
*/
#ifndef DEBUG_OUTPUT
#define DEBUG_OUTPUT 0
#endif
#if DEBUG_OUTPUT
#define debug(x) gck_rpc_debug x
#else
#define debug(x)
#endif
#define warning(x) gck_rpc_warn x
/* -----------------------------------------------------------------------------
* TLS-PSK (pre-shared key) functionality
*/
/* Utility function to decode a single hex char.
*
* Returns value as integer, or -1 on invalid hex char (not 0-9, a-f or A-F).
*/
static int
_tls_psk_to_hex(char val)
{
if (val >= '0' && val <= '9')
return val - '0';
if (val >= 'a' && val <= 'f')
return val - 'a' + 10;
if (val >= 'A' && val <= 'F')
return val - 'A' + 10;
return -1;
}
/* Hex decode the key from an entry in the TLS-PSK key file. Entrys are of the form
*
* identity:hex-key\n
*
* Logging debug/error messages here is a bit problematic since the key is sensitive
* and should not be logged to syslog for example. This code avoids logging the key
* part and only logs identity.
*
* Returns 0 on failure, number of bytes in hex-decoded key on success.
*/
static int
_tls_psk_decode_key(const char *identity, const char *hexkey, unsigned char *psk, unsigned int max_psk_len)
{
int psk_len, i;
/* check that length of the key is even */
if ((strlen(hexkey) % 2) != 0) {
warning(("un-even length TLS-PSK key"));
return 0;
}
memset(psk, 0, max_psk_len);
psk_len = 0;
while (*hexkey && (psk_len < max_psk_len)) {
/* decode first half of byte, check for errors */
if ((i = _tls_psk_to_hex(*hexkey)) < 0) {
warning(("bad TLS-PSK '%.100s' hex char at position %i (%c)",
identity, psk_len + 1, *hexkey));
return 0;
}
*psk = i << 4;
hexkey++;
/* decode second half of byte, check for errors */
if ((i = _tls_psk_to_hex(*hexkey)) < 0) {
warning(("bad TLS-PSK '%.100s' hex char at position %i (%c)",
identity, psk_len + 1, *hexkey));
return 0;
}
*psk |= i;
hexkey++;
psk_len++;
psk++;
}
if (*hexkey) {
warning(("too long TLS-PSK '%.100s' key (max %i)", identity, max_psk_len));
return 0;
}
return psk_len;
}
/*
* Callbacks invoked by OpenSSL PSK initialization.
*/
/* Server side TLS-PSK initialization callback. Given an identity (chosen by the client),
* locate a pre-shared key and put it in psk.
*
* Returns the number of bytes put in psk, or 0 on failure.
*/
static unsigned int
_tls_psk_server_cb(SSL *ssl, const char *identity,
unsigned char *psk, unsigned int max_psk_len)
{
char line[1024], *hexkey;
unsigned int psk_len;
FILE *fd;
int i;
debug(("Initializing TLS-PSK with keyfile '%.100s', identity '%.100s'",
tls_psk_key_filename, identity));
if ((fd = fopen(tls_psk_key_filename, "r")) == NULL) {
gck_rpc_warn("can't open TLS-PSK keyfile '%.100s' for reading", tls_psk_key_filename);
return 0;
}
/* Format of PSK file is that of GnuTLS psktool.
*
* identity:hex-key
* other:another-hex-key
*/
psk_len = 0;
while (fgets(line, sizeof(line) - 1, fd)) {
/* Find first colon and replace it with NULL */
hexkey = strchr(line, ':');
if (! hexkey)
continue;
*hexkey = 0;
hexkey++;
/* Remove newline(s) at the end */
for (i = strlen(hexkey) - 1; i && (hexkey[i] == '\n' || hexkey[i] == '\r'); i--)
hexkey[i] = 0;
if (identity == NULL || ! identity[0] || ! strcmp(line, identity)) {
/* If the line starts with identity: or identity is not provided, parse this line. */
psk_len = _tls_psk_decode_key(line, hexkey, psk, max_psk_len);
if (psk_len)
debug(("Loaded TLS-PSK '%.100s' from keyfile '%.100s'",
line, tls_psk_key_filename));
else
warning(("Failed loading TLS-PSK '%.100s' from keyfile '%.100s'",
line, tls_psk_key_filename));
break;
}
}
fclose(fd);
return psk_len;
}
/* Client side TLS-PSK initialization callback. Indicate to OpenSSL what identity to
* use, and the pre-shared key for that identity.
*
* Returns the number of bytes put in psk, or 0 on failure.
*/
static unsigned int
_tls_psk_client_cb(SSL *ssl, const char *hint,
char *identity, unsigned int max_identity_len,
unsigned char *psk, unsigned int max_psk_len)
{
/* Client tells server which identity it wants to use in ClientKeyExchange */
snprintf(identity, max_identity_len, "%s", tls_psk_identity);
/* We currently just discard the hint sent to us by the server */
return _tls_psk_server_cb(ssl, identity, psk, max_psk_len);
}
/* Initialize OpenSSL and create an SSL CTX. Should be called just once.
*
* Returns 0 on failure and 1 on success.
*/
int
gck_rpc_init_tls_psk(GckRpcTlsPskState *state, const char *key_filename,
const char *identity, enum gck_rpc_tls_psk_caller caller)
{
char *tls_psk_ciphers = PKCS11PROXY_TLS_PSK_CIPHERS;
if (state->initialized == 1) {
warning(("TLS state already initialized"));
return 0;
}
/* Global OpenSSL initialization */
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_ssl_algorithms();
assert(caller == GCK_RPC_TLS_PSK_CLIENT || caller == GCK_RPC_TLS_PSK_SERVER);
if (caller == GCK_RPC_TLS_PSK_CLIENT)
state->ssl_ctx = SSL_CTX_new(SSLv23_client_method());
else
state->ssl_ctx = SSL_CTX_new(SSLv23_server_method());
if (state->ssl_ctx == NULL) {
gck_rpc_warn("can't initialize SSL_CTX");
return 0;
}
/* Set up callback for TLS-PSK initialization */
if (caller == GCK_RPC_TLS_PSK_CLIENT)
SSL_CTX_set_psk_client_callback(state->ssl_ctx, _tls_psk_client_cb);
else
SSL_CTX_set_psk_server_callback(state->ssl_ctx, _tls_psk_server_cb);
/* Disable compression, for security (CRIME Attack). */
SSL_CTX_set_options(state->ssl_ctx, SSL_OP_NO_COMPRESSION);
/* Specify ciphers to use */
SSL_CTX_set_cipher_list(state->ssl_ctx, tls_psk_ciphers);
snprintf(tls_psk_key_filename, sizeof(tls_psk_key_filename), "%s", key_filename);
snprintf(tls_psk_identity, sizeof(tls_psk_identity), "%s", identity ? identity : "");
state->type = caller;
state->initialized = 1;
debug(("Initialized TLS-PSK %s", caller == GCK_RPC_TLS_PSK_CLIENT ? "client" : "server"));
return 1;
}
/* Set up SSL for a new socket. Call this after accept() or connect().
*
* When a socket has been created, call gck_rpc_start_tls() with the TLS state
* initialized using gck_rpc_init_tls_psk() and the new socket.
*
* Returns 1 on success and 0 on failure.
*/
int
gck_rpc_start_tls(GckRpcTlsPskState *state, int sock)
{
int res;
char buf[256];
state->ssl = SSL_new(state->ssl_ctx);
if (! state->ssl) {
warning(("can't initialize SSL"));
return 0;
}
state->bio = BIO_new_socket(sock, BIO_NOCLOSE);
if (! state->bio) {
warning(("can't initialize SSL BIO"));
return 0;
}
SSL_set_bio(state->ssl, state->bio, state->bio);
/* Set up callback for TLS-PSK initialization */
if (state->type == GCK_RPC_TLS_PSK_CLIENT)
res = SSL_connect(state->ssl);
else
res = SSL_accept(state->ssl);
if (res != 1) {
ERR_error_string_n(ERR_get_error(), buf, sizeof(buf));
warning(("can't start TLS : %i/%i (%s perhaps)",
res, SSL_get_error(state->ssl, res), strerror(errno)));
warning(("SSL ERR: %s", buf));
return 0;
}
return 1;
}
/* Un-initialize everything SSL related. Call this on application shut down.
*/
void
gck_rpc_close_tls(GckRpcTlsPskState *state)
{
if (state->ssl_ctx) {
SSL_CTX_free(state->ssl_ctx);
state->ssl_ctx = NULL;
}
if (state->ssl) {
SSL_free(state->ssl);
state->ssl = NULL;
}
if (state->bio) {
BIO_free(state->bio);
state->bio = NULL;
}
}
/* Send data using SSL.
*
* Returns the number of bytes written.
*/
int
gck_rpc_tls_write_all(GckRpcTlsPskState *state, void *data, unsigned int len)
{
int bytes, error;
char buf[256];
assert(state);
assert(data);
assert(len > 0);
bytes = SSL_write(state->ssl, data, len);
if (bytes <= 0) {
while ((error = ERR_get_error())) {
ERR_error_string_n(error, buf, sizeof(buf));
warning(("SSL_write error: %s", buf));
}
return 0;
}
return bytes;
}
/* Read data using SSL.
*
* Returns the number of bytes read.
*/
int
gck_rpc_tls_read_all(GckRpcTlsPskState *state, void *data, unsigned int len)
{
int bytes, error;
char buf[256];
assert(state);
assert(data);
assert(len > 0);
bytes = SSL_read(state->ssl, data, len);
if (bytes <= 0) {
while ((error = ERR_get_error())) {
ERR_error_string_n(error, buf, sizeof(buf));
warning(("SSL_read error: %s", buf));
}
return 0;
}
return bytes;
}
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
#ifndef GCKRPC_TLS_PSK_H_
#define GCKRPC_TLS_PSK_H_
#include "openssl/bio.h"
#include "openssl/ssl.h"
#include "openssl/err.h"
#if OPENSSL_VERSION_NUMBER < 0x10000000
# error "OpenSSL version >= 1.0.0 required"
#endif
enum gck_rpc_tls_psk_caller {
GCK_RPC_TLS_PSK_CLIENT,
GCK_RPC_TLS_PSK_SERVER
};
typedef struct {
int initialized;
SSL_CTX *ssl_ctx;
BIO *bio;
SSL *ssl;
enum gck_rpc_tls_psk_caller type;
} GckRpcTlsPskState;
int gck_rpc_init_tls_psk(GckRpcTlsPskState *state, const char *key_filename,
const char *identity, enum gck_rpc_tls_psk_caller caller);
int gck_rpc_start_tls(GckRpcTlsPskState *state, int sock);
int gck_rpc_tls_write_all(GckRpcTlsPskState *state, void *data, unsigned int len);
int gck_rpc_tls_read_all(GckRpcTlsPskState *state, void *data, unsigned int len);
void gck_rpc_close_tls(GckRpcTlsPskState *state);
#endif /* GCKRPC_TLS_PSK_H_ */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment