The TLS private keys are kept into a privileged process.

That process is called security-module (sec-mod) and communicates
with the workers using a unix domain socket.
This commit is contained in:
Nikos Mavrogiannopoulos
2013-03-15 17:54:36 +01:00
parent 73a532b5c5
commit 5a4ce846b7
25 changed files with 612 additions and 212 deletions

1
.gitignore vendored
View File

@@ -34,3 +34,4 @@ gl/stdlib.h
gl/sys/stat.h
gl/sys/time.h
gl/sys/types.h
src/ocpasswd

5
NEWS
View File

@@ -1,4 +1,4 @@
* Version 0.0.3 (unreleased)
* Version 0.1.0 (unreleased)
- Corrected issue with ocsp-response configuration field.
- Added ability to specify multiple certificate and key pairs.
@@ -6,6 +6,9 @@
- Added the "plain" authentication option, which allows a simple password
file format. The ocpasswd tool can be used to generate entries for this
file.
- The private key operations are performed on a special process to
prevent loss of the private key in case of compromise of a worker
process.
* Version 0.0.2 (released 2013-03-05)

9
TODO
View File

@@ -10,15 +10,6 @@
* Handle users being in multiple groups.
* Keep the TLS key and certificates into the privileged process and use IPC
for operations. This currently cannot be done, because the main process is
the one that spawns the workers and there is no sane way to make the key
(or any temporal value depending on it) disappear from the main process
memory. Moreover, such a move will make the privileged process a bottleneck
on a multi-core system. If that functionality is desirable, it can be
obtained using a software security module (e.g. pkcs11 softoken), an HSM
or TPM.
* Certificate authentication to the main process. Possibly that is just
wishful thinking. To verify the TLS client certificate verify signature one
needs instead of the signature, the contents of all the handshake messages,

View File

@@ -30,7 +30,7 @@ fi
AC_CHECK_SIZEOF([unsigned long])
AC_C_BIGENDIAN
PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= 3.0.28])
PKG_CHECK_MODULES([LIBGNUTLS], [gnutls >= 3.1.9])
AC_CHECK_LIB(gnutls, gnutls_pkcs11_reinit, [
AC_DEFINE([HAVE_PKCS11], [], [PKCS11 detected in gnutls])
])

View File

@@ -1,4 +1,4 @@
EXTRA_DIST = design.dia sample.config scripts/ocserv-script
EXTRA_DIST = design.dia sample.config scripts/ocserv-script sample.passwd
dist_man_MANS = ocserv.8

Binary file not shown.

View File

@@ -2,7 +2,7 @@
# all should succeed.
# Options: certificate, pam.
#auth = "certificate"
auth = "plain[./passwd]"
auth = "plain[./sample.passwd]"
#auth = "pam"
# A banner to be displayed on clients
@@ -114,6 +114,9 @@ use-utmp = true
# PID file
pid-file = /var/run/ocserv.pid
# socket file
socket-file = /var/run/ocserv-socket
run-as-user = nobody
run-as-group = nogroup

1
doc/sample.passwd Normal file
View File

@@ -0,0 +1 @@
test:tost:$5$i6SNmLDCgBNjyJ7q$SZ4bVJb7I/DLgXo3txHBVohRFBjOtdbxGQZp.DOnrA.

View File

@@ -17,7 +17,8 @@ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \
vpn.h cookies.h tlslib.h http-parser/http_parser.h log.c tun.c tun.h \
config.c pam.c pam.h worker-resume.c worker.h main-resume.c main.h \
main-user.c cookies-gdbm.c cookies-hash.c worker-misc.c setproctitle.h \
setproctitle.c worker-privs.c plain.c plain.h \
setproctitle.c worker-privs.c plain.c plain.h common.h common.c \
sec-mod.c sec-mod.h \
$(CCAN_SOURCES)
ocserv_SOURCES += ocserv-args.def ocserv-args.c ocserv-args.h

44
src/common.c Normal file
View File

@@ -0,0 +1,44 @@
/*
* Copyright (C) 2013 Nikos Mavrogiannopoulos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <stdint.h>
#include <errno.h>
#include <unistd.h>
ssize_t force_write(int sockfd, const void *buf, size_t len)
{
int left = len;
int ret;
const uint8_t * p = buf;
while(left > 0) {
ret = write(sockfd, p, left);
if (ret == -1) {
if (errno != EAGAIN && errno != EINTR)
return ret;
}
if (ret > 0) {
left -= ret;
p += ret;
}
}
return len;
}

6
src/common.h Normal file
View File

@@ -0,0 +1,6 @@
#ifndef COMMON_H
# define COMMON_H
ssize_t force_write(int sockfd, const void *buf, size_t len);
#endif

View File

@@ -179,6 +179,8 @@ unsigned j;
if (pid_file == NULL)
READ_STRING("pid-file", pid_file, 0);
READ_STRING("socket-file", config->socket_file_prefix, 1);
READ_STRING("banner", config->banner, 0);
READ_TF("always-require-cert", config->force_cert_auth, 0, 1);
READ_TF("use-utmp", config->use_utmp, 0, 1);
@@ -353,6 +355,7 @@ unsigned i;
DEL(config->xml_config_hash);
DEL(config->cert_hash);
#endif
DEL(config->socket_file_prefix);
DEL(config->plain_passwd);
DEL(config->ocsp_response);
DEL(config->banner);
@@ -411,3 +414,11 @@ FILE* fp;
fprintf(fp, "%u", (unsigned)getpid());
fclose(fp);
}
void remove_pid_file(void)
{
if (pid_file==NULL)
return;
remove(pid_file);
}

View File

@@ -34,7 +34,11 @@
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <tlslib.h>
#include <sys/un.h>
#include <cloexec.h>
#include "ipc.h"
#include "setproctitle.h"
#include <sec-mod.h>
#include <vpn.h>
#include <cookies.h>
@@ -372,3 +376,62 @@ struct banned_st *btmp;
list_add(&s->ban_list.head, &(btmp->list));
}
void run_sec_mod(main_server_st * s)
{
struct sockaddr_un sa;
int sd, e, ret;
pid_t pid;
sa.sun_family = AF_UNIX;
/* make socket name */
snprintf(s->socket_file, sizeof(s->socket_file), "%s.%u", s->config->socket_file_prefix, (unsigned)getpid());
snprintf(sa.sun_path, sizeof(sa.sun_path), "%s", s->socket_file);
remove(s->socket_file);
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd == -1) {
e = errno;
mslog(s, NULL, LOG_ERR, "could not create socket '%s': %s", s->socket_file, strerror(e));
exit(1);
}
ret = bind(sd, (struct sockaddr *)&sa, sizeof(sa));
if (ret == -1) {
e = errno;
mslog(s, NULL, LOG_ERR, "could not bind socket '%s': %s", s->socket_file, strerror(e));
exit(1);
}
ret = chown(sa.sun_path, s->config->uid, s->config->gid);
if (ret == -1) {
e = errno;
mslog(s, NULL, LOG_ERR, "could not chown socket '%s': %s", s->socket_file, strerror(e));
}
ret = listen(sd, 1024);
if (ret == -1) {
e = errno;
mslog(s, NULL, LOG_ERR, "could not listen to socket '%s': %s", s->socket_file, strerror(e));
exit(1);
}
set_cloexec_flag (sd, 1);
pid = fork();
if (pid == 0) { /* child */
setproctitle(PACKAGE_NAME"-secmod");
sec_mod_server(s->config, sd);
exit(0);
} else if (pid > 0) { /* parent */
close(sd);
s->sec_mod_pid = pid;
} else {
e = errno;
mslog(s, NULL, LOG_ERR, "error in fork(): %s", strerror(e));
exit(1);
}
}

View File

@@ -313,6 +313,11 @@ struct script_wait_st *stmp, *spos;
while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
estatus = WEXITSTATUS(status);
if (pid == s->sec_mod_pid) {
mslog(s, NULL, LOG_ERR, "ocserv-secmod died unexpectedly");
terminate = 1;
}
/* check if someone was waiting for that pid */
list_for_each_safe(&s->script_list.head, stmp, spos, list) {
@@ -425,6 +430,8 @@ static void kill_children(main_server_st* s)
{
struct proc_st *ctmp;
/* kill the security module server */
kill(s->sec_mod_pid, SIGTERM);
list_for_each(&s->clist.head, ctmp, list) {
if (ctmp->pid != -1) {
kill(ctmp->pid, SIGTERM);
@@ -536,7 +543,6 @@ static void check_other_work(main_server_st *s)
if (reload_conf != 0) {
mslog(s, NULL, LOG_INFO, "HUP signal was received; reloading configuration");
reload_cfg_file(s->config);
tls_global_init_certs(s);
reload_conf = 0;
}
@@ -544,6 +550,8 @@ static void check_other_work(main_server_st *s)
mslog(s, NULL, LOG_DEBUG, "termination signal received; waiting for children to die");
kill_children(s);
closelog();
remove(s->socket_file);
remove_pid_file();
while (waitpid(-1, NULL, 0) > 0);
exit(0);
}
@@ -656,9 +664,6 @@ int main(int argc, char** argv)
#endif
memset(&ws, 0, sizeof(ws));
/* Initialize GnuTLS */
tls_global_init_certs(&s);
if (config.foreground == 0) {
if (daemon(0, 0) == -1) {
e = errno;
@@ -668,6 +673,11 @@ int main(int argc, char** argv)
}
write_pid_file();
run_sec_mod(&s);
/* Initialize certificates */
tls_global_init_certs(&s);
alarm(MAINTAINANCE_TIME(&s));
@@ -726,7 +736,7 @@ int main(int argc, char** argv)
continue;
}
set_cloexec_flag (fd, 1);
/* Check if the client is on the banned list */
ret = check_if_banned(&s, &ws.remote_addr, ws.remote_addr_len);
if (ret < 0) {
@@ -774,10 +784,6 @@ int main(int argc, char** argv)
ws.conn_fd = fd;
ws.creds = &s.creds;
ret = tls_global_init_client(&ws);
if (ret < 0)
exit(1);
/* Drop privileges after this point */
drop_privileges(&s);

View File

@@ -15,6 +15,7 @@
int cmd_parser (int argc, char **argv, struct cfg_st* config);
void reload_cfg_file(struct cfg_st* config);
void write_pid_file(void);
void remove_pid_file(void);
/* set to 1 to start cleaning up cookies, sessions etc. */
extern unsigned int need_maintainance;
@@ -102,6 +103,9 @@ typedef struct main_server_st {
struct script_list_st script_list;
struct ban_list_st ban_list;
char socket_file[_POSIX_PATH_MAX];
pid_t sec_mod_pid;
unsigned active_clients;
} main_server_st;
@@ -155,4 +159,6 @@ int check_if_banned(main_server_st* s, struct sockaddr_storage *addr, socklen_t
int handle_script_exit(main_server_st *s, struct proc_st* proc, int code);
void run_sec_mod(main_server_st * s);
#endif

View File

@@ -2,7 +2,7 @@
*
* DO NOT EDIT THIS FILE (ocserv-args.c)
*
* It has been AutoGen-ed March 14, 2013 at 01:42:30 PM by AutoGen 5.16
* It has been AutoGen-ed March 15, 2013 at 04:54:18 PM by AutoGen 5.16
* From the definitions ocserv-args.def
* and the template file options
*

View File

@@ -203,6 +203,9 @@ use-utmp = true
# PID file
pid-file = /var/run/ocserv.pid
# socket file used for IPC, will be appended with .PID
socket-file = /var/run/ocserv-socket
# The user the worker processes will be run as.
run-as-user = nobody
run-as-group = nogroup
@@ -345,10 +348,9 @@ doc-section = {
ds-format = 'texi';
ds-text = <<-_EOT_
Note that while this server utilizes privilege separation for password
authentication, this does not apply for TLS and client certificate authentication.
This has the advantage of spreading TLS calculations to multiple workers (i.e. cores)
if available, but at the cost of each worker having a copy of the server's
private key.
authentication, this does not apply for TLS client certificate authentication.
That is because the worker has no easy way to prove to the main server that
it performed the certificate verification.
_EOT_;
};

View File

@@ -2,7 +2,7 @@
*
* DO NOT EDIT THIS FILE (ocserv-args.h)
*
* It has been AutoGen-ed March 14, 2013 at 01:42:30 PM by AutoGen 5.16
* It has been AutoGen-ed March 15, 2013 at 04:54:18 PM by AutoGen 5.16
* From the definitions ocserv-args.def
* and the template file options
*

264
src/sec-mod.c Normal file
View File

@@ -0,0 +1,264 @@
/*
* Copyright (C) 2013 Nikos Mavrogiannopoulos
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program 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 General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/select.h>
#include <sys/wait.h>
#include <fcntl.h>
#include <sys/socket.h>
#include <netdb.h>
#include <signal.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/un.h>
#include <common.h>
#include <syslog.h>
#include <vpn.h>
#include <tlslib.h>
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
#define MAX_PIN_SIZE GNUTLS_PKCS11_MAX_PIN_LEN
struct pin_st {
char pin[MAX_PIN_SIZE];
char srk_pin[MAX_PIN_SIZE];
};
static
int pin_callback (void *user, int attempt, const char *token_url,
const char *token_label, unsigned int flags, char *pin,
size_t pin_max)
{
struct pin_st * ps = user;
int srk = 0;
const char* p;
unsigned len;
if (flags & GNUTLS_PIN_FINAL_TRY) {
syslog(LOG_ERR, "PIN callback: final try before locking; not attempting to unlock");
return -1;
}
if (flags & GNUTLS_PIN_WRONG) {
syslog(LOG_ERR, "PIN callback: wrong PIN was entered for '%s' (%s)", token_label, token_url);
return -1;
}
if (ps->pin[0] == 0) {
syslog(LOG_ERR, "PIN required for '%s' but pin-file was not set", token_label);
return -1;
}
if (strcmp(token_url, "SRK") == 0 || strcmp(token_label, "SRK") == 0) {
srk = 1;
p = ps->srk_pin;
} else {
p = ps->pin;
}
if (srk != 0 && ps->srk_pin[0] == 0) {
syslog(LOG_ERR, "PIN required for '%s' but srk-pin-file was not set", token_label);
return -1;
}
len = strlen(p);
if (len > pin_max-1) {
syslog(LOG_ERR, "Too long PIN (%u chars)", len);
return -1;
}
memcpy(pin, p, len);
pin[len] = 0;
return 0;
}
static
int load_pins(struct cfg_st* config, struct pin_st* s)
{
int fd, ret;
s->srk_pin[0] = 0;
s->pin[0] = 0;
if (config->srk_pin_file != NULL) {
fd = open(config->srk_pin_file, O_RDONLY);
if (fd < 0) {
syslog(LOG_ERR, "could not open SRK PIN file '%s'", config->srk_pin_file);
return -1;
}
ret = read(fd, s->srk_pin, sizeof(s->srk_pin));
close(fd);
if (ret <= 1) {
syslog(LOG_ERR, "could not read from PIN file '%s'", config->srk_pin_file);
return -1;
}
if (s->srk_pin[ret-1] == '\n' || s->srk_pin[ret-1] == '\r')
s->srk_pin[ret-1] = 0;
s->srk_pin[ret] = 0;
}
if (config->pin_file != NULL) {
fd = open(config->pin_file, O_RDONLY);
if (fd < 0) {
syslog(LOG_ERR, "could not open PIN file '%s'", config->pin_file);
return -1;
}
ret = read(fd, s->pin, sizeof(s->pin));
close(fd);
if (ret <= 1) {
syslog(LOG_ERR, "could not read from PIN file '%s'", config->pin_file);
return -1;
}
if (s->pin[ret-1] == '\n' || s->pin[ret-1] == '\r')
s->pin[ret-1] = 0;
s->pin[ret] = 0;
}
return 0;
}
void
sec_mod_server(struct cfg_st* config, int sd)
{
struct sockaddr_un sa;
socklen_t sa_len;
int cfd, ret, e;
unsigned i, buffer_size, type;
gnutls_privkey_t *key;
uint8_t *buffer;
unsigned key_size = config->key_size;
struct pin_st pins;
gnutls_datum_t data, out;
uint16_t length;
signal(SIGHUP, SIG_IGN);
signal(SIGINT, SIG_DFL);
signal(SIGTERM, SIG_DFL);
buffer_size = 8*1024;
buffer = malloc(buffer_size);
if (buffer == NULL) {
syslog(LOG_ERR, "error in memory allocation");
exit(1);
}
ret = load_pins(config, &pins);
if (ret < 0) {
syslog(LOG_ERR, "error loading PIN files");
exit(1);
}
key = malloc(sizeof(*key)*config->key_size);
if (key == NULL) {
syslog(LOG_ERR, "error in memory allocation");
exit(1);
}
/* read private keys */
for (i=0;i<key_size;i++) {
ret = gnutls_privkey_init(&key[i]);
GNUTLS_FATAL_ERR(ret);
/* load the private key */
if (gnutls_url_is_supported(config->key[i]) != 0) {
gnutls_privkey_set_pin_function (key[i], pin_callback, &pins);
ret = gnutls_privkey_import_url(key[i], config->key[i], 0);
GNUTLS_FATAL_ERR(ret);
} else {
ret = gnutls_load_file(config->key[i], &data);
if (ret < 0) {
syslog(LOG_ERR, "error loading file '%s'", config->key[i]);
GNUTLS_FATAL_ERR(ret);
}
ret = gnutls_privkey_import_x509_raw(key[i], &data, GNUTLS_X509_FMT_PEM, NULL, 0);
GNUTLS_FATAL_ERR(ret);
gnutls_free(data.data);
}
}
for (;;) {
sa_len = sizeof(sa);
cfd = accept(sd, (struct sockaddr *)&sa, &sa_len);
if (cfd == -1) {
e = errno;
syslog(LOG_ERR, "error accepting sec-mod connection: %s", strerror(e));
continue;
}
/* read request */
ret = recv(cfd, buffer, buffer_size, 0);
if (ret <= 2) {
e = errno;
syslog(LOG_ERR, "error receiving sec-mod data: %s", strerror(e));
goto cont;
}
/* calculate */
i = buffer[0];
type = buffer[1];
if (i >= key_size) {
syslog(LOG_ERR, "sec-mod received out-of-bounds key index");
goto cont;
}
data.data = &buffer[2];
data.size = ret - 2;
#if GNUTLS_VERSION_NUMBER >= 0x03010a
if (type == 'S') {
ret = gnutls_privkey_sign_raw_data(key[i], 0, &data, &out);
} else
#endif
if (type == 'D') {
ret = gnutls_privkey_decrypt_data(key[i], 0, &data, &out);
} else {
syslog(LOG_ERR, "unknown type 0x%.2x", type);
goto cont;
}
if (ret < 0) {
syslog(LOG_ERR, "sec-mod error in crypto operation: %s", gnutls_strerror(ret));
goto cont;
}
/* write reply */
length = out.size;
force_write(cfd, &length, 2);
force_write(cfd, out.data, out.size);
gnutls_free(out.data);
cont:
close(cfd);
}
}

5
src/sec-mod.h Normal file
View File

@@ -0,0 +1,5 @@
#ifndef SEC_MOD_H
void sec_mod_server(struct cfg_st* config, int sd);
#endif

View File

@@ -21,6 +21,7 @@
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <gnutls/pkcs11.h>
#include <gnutls/abstract.h>
#include <errno.h>
#include <stdlib.h>
#include <stdarg.h>
@@ -36,6 +37,7 @@
#include <vpn.h>
#include <main.h>
#include <worker.h>
#include <sys/un.h>
ssize_t tls_send(gnutls_session_t session, const void *data,
@@ -241,103 +243,6 @@ fail:
}
int pin_callback (void *user, int attempt, const char *token_url,
const char *token_label, unsigned int flags, char *pin,
size_t pin_max)
{
struct tls_st * ts = user;
int srk = 0;
const char* p;
unsigned len;
if (flags & GNUTLS_PIN_FINAL_TRY) {
syslog(LOG_ERR, "PIN callback: final try before locking; not attempting to unlock");
return -1;
}
if (flags & GNUTLS_PIN_WRONG) {
syslog(LOG_ERR, "PIN callback: wrong PIN was entered for '%s' (%s)", token_label, token_url);
return -1;
}
if (ts->pin[0] == 0) {
syslog(LOG_ERR, "PIN required for '%s' but pin-file was not set", token_label);
return -1;
}
if (strcmp(token_url, "SRK") == 0 || strcmp(token_label, "SRK") == 0) {
srk = 1;
p = ts->srk_pin;
} else {
p = ts->pin;
}
if (srk != 0 && ts->srk_pin[0] == 0) {
syslog(LOG_ERR, "PIN required for '%s' but srk-pin-file was not set", token_label);
return -1;
}
len = strlen(p);
if (len > pin_max-1) {
syslog(LOG_ERR, "Too long PIN (%u chars)", len);
return -1;
}
memcpy(pin, p, len);
pin[len] = 0;
return 0;
}
static
int load_pins(main_server_st* s)
{
int fd, ret;
s->creds.srk_pin[0] = 0;
s->creds.pin[0] = 0;
if (s->config->srk_pin_file != NULL) {
fd = open(s->config->srk_pin_file, O_RDONLY);
if (fd < 0) {
mslog(s, NULL, LOG_ERR, "could not open SRK PIN file '%s'", s->config->srk_pin_file);
return -1;
}
ret = read(fd, s->creds.srk_pin, sizeof(s->creds.srk_pin));
close(fd);
if (ret <= 1) {
mslog(s, NULL, LOG_ERR, "could not read from PIN file '%s'", s->config->srk_pin_file);
return -1;
}
if (s->creds.srk_pin[ret-1] == '\n' || s->creds.srk_pin[ret-1] == '\r')
s->creds.srk_pin[ret-1] = 0;
s->creds.srk_pin[ret] = 0;
}
if (s->config->pin_file != NULL) {
fd = open(s->config->pin_file, O_RDONLY);
if (fd < 0) {
mslog(s, NULL, LOG_ERR, "could not open PIN file '%s'", s->config->pin_file);
return -1;
}
ret = read(fd, s->creds.pin, sizeof(s->creds.pin));
close(fd);
if (ret <= 1) {
mslog(s, NULL, LOG_ERR, "could not read from PIN file '%s'", s->config->pin_file);
return -1;
}
if (s->creds.pin[ret-1] == '\n' || s->creds.pin[ret-1] == '\r')
s->creds.pin[ret-1] = 0;
s->creds.pin[ret] = 0;
}
return 0;
}
void tls_global_init(main_server_st* s)
{
int ret;
@@ -419,11 +324,175 @@ int ret;
}
}
struct key_cb_data {
unsigned idx; /* the index of the key */
struct sockaddr_un sa;
};
static
int key_cb_common_func (gnutls_privkey_t key, void* userdata, const gnutls_datum_t * raw_data,
gnutls_datum_t * output, unsigned type)
{
struct key_cb_data* cdata = userdata;
int sd, ret, e;
uint8_t header[2];
struct iovec iov[2];
uint16_t length;
output->data = NULL;
sd = socket(AF_UNIX, SOCK_STREAM, 0);
if (sd == -1) {
e = errno;
syslog(LOG_ERR, "error opening socket: %s", strerror(e));
return -1;
}
ret = connect(sd, (struct sockaddr *)&cdata->sa, sizeof(cdata->sa));
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "error connecting to sec-mod socket '%s': %s",
cdata->sa.sun_path, strerror(e));
return -1;
}
header[0] = cdata->idx;
header[1] = type;
iov[0].iov_base = header;
iov[0].iov_len = sizeof(header);
iov[1].iov_base = raw_data->data;
iov[1].iov_len = raw_data->size;
ret = writev(sd, iov, 2);
if (ret == -1) {
e = errno;
syslog(LOG_ERR, "error writing to sec-mod: %s", strerror(e));
goto error;
}
ret = recv(sd, &length, 2, 0);
if (ret < 2) {
e = errno;
syslog(LOG_ERR, "error reading from sec-mod: %s", strerror(e));
goto error;
}
output->size = length;
output->data = gnutls_malloc(output->size);
if (output->data == NULL) {
syslog(LOG_ERR, "error allocating memory");
goto error;
}
ret = recv(sd, output->data, output->size, 0);
if (ret <= 0) {
e = errno;
syslog(LOG_ERR, "error reading from sec-mod: %s", strerror(e));
goto error;
}
output->size = ret;
close(sd);
return 0;
error:
close(sd);
gnutls_free(output->data);
return -1;
}
#if GNUTLS_VERSION_NUMBER >= 0x03010a
static
int key_cb_sign_func (gnutls_privkey_t key, void* userdata, const gnutls_datum_t * raw_data,
gnutls_datum_t * signature)
{
return key_cb_common_func(key, userdata, raw_data, signature, 'S');
}
#else
# define key_cb_sign_func NULL
#endif
static int key_cb_decrypt_func(gnutls_privkey_t key, void* userdata, const gnutls_datum_t * ciphertext,
gnutls_datum_t * plaintext)
{
return key_cb_common_func(key, userdata, ciphertext, plaintext, 'D');
}
static void key_cb_deinit_func(gnutls_privkey_t key, void* userdata)
{
free(userdata);
}
static
int load_key_files(main_server_st *s)
{
int ret;
gnutls_pcert_st *pcert_list;
unsigned pcert_list_size, i;
gnutls_privkey_t key;
gnutls_datum_t data;
struct key_cb_data * cdata;
for (i=0;i<s->config->key_size;i++) {
/* load the certificate */
if (gnutls_url_is_supported(s->config->cert[i]) != 0) {
mslog(s, NULL, LOG_ERR, "Loading a certificate from '%s' is unsupported", s->config->cert[i]);
return -1;
} else {
ret = gnutls_load_file(s->config->cert[i], &data);
if (ret < 0) {
mslog(s, NULL, LOG_ERR, "error loading file '%s'", s->config->key[i]);
GNUTLS_FATAL_ERR(ret);
}
pcert_list_size = 8;
pcert_list = gnutls_malloc(sizeof(pcert_list[0])*pcert_list_size);
if (pcert_list == NULL) {
mslog(s, NULL, LOG_ERR, "error allocating memory");
return -1;
}
ret = gnutls_pcert_list_import_x509_raw(pcert_list, &pcert_list_size,
&data, GNUTLS_X509_FMT_PEM, GNUTLS_X509_CRT_LIST_FAIL_IF_UNSORTED|GNUTLS_X509_CRT_LIST_IMPORT_FAIL_IF_EXCEED);
GNUTLS_FATAL_ERR(ret);
gnutls_free(data.data);
}
ret = gnutls_privkey_init(&key);
GNUTLS_FATAL_ERR(ret);
cdata = malloc(sizeof(*cdata));
if (cdata == NULL) {
mslog(s, NULL, LOG_ERR, "error allocating memory");
return -1;
}
cdata->idx = i;
cdata->sa.sun_family = AF_UNIX;
snprintf(cdata->sa.sun_path, sizeof(cdata->sa.sun_path), "%s", s->socket_file);
/* load the private key */
ret = gnutls_privkey_import_ext2(key, gnutls_pubkey_get_pk_algorithm(pcert_list[0].pubkey, NULL),
cdata, key_cb_sign_func, key_cb_decrypt_func,
key_cb_deinit_func, GNUTLS_PRIVKEY_IMPORT_AUTO_RELEASE);
GNUTLS_FATAL_ERR(ret);
ret = gnutls_certificate_set_key(s->creds.xcred, NULL, 0, pcert_list,
pcert_list_size, key);
GNUTLS_FATAL_ERR(ret);
}
return 0;
}
/* reload key files etc. */
void tls_global_init_certs(main_server_st* s)
{
int ret;
unsigned i;
const char* perr;
if (s->config->tls_debug) {
@@ -437,14 +506,7 @@ const char* perr;
ret = gnutls_certificate_allocate_credentials(&s->creds.xcred);
GNUTLS_FATAL_ERR(ret);
ret = load_pins(s);
if (ret < 0) {
exit(1);
}
set_dh_params(s, s->creds.xcred);
gnutls_certificate_set_pin_function (s->creds.xcred, pin_callback, &s->creds);
if (s->config->key_size == 0 || s->config->cert_size == 0) {
mslog(s, NULL, LOG_ERR, "no certificate or key files were specified.\n");
@@ -453,22 +515,10 @@ const char* perr;
certificate_check(s);
for (i=0;i<s->config->key_size;i++) {
if (strncmp(s->config->key[i], "pkcs11:", 7) != 0) {
ret =
gnutls_certificate_set_x509_key_file(s->creds.xcred, s->config->cert[i],
s->config->key[i], GNUTLS_X509_FMT_PEM);
if (ret < 0) {
mslog(s, NULL, LOG_ERR, "error setting the certificate (%s) or key (%s) files: %s\n",
s->config->cert[i], s->config->key[i], gnutls_strerror(ret));
exit(1);
}
} else {
#ifndef HAVE_PKCS11
mslog(s, NULL, LOG_ERR, "cannot load key, GnuTLS is compiled without pkcs11 support\n");
exit(1);
#endif
}
ret = load_key_files(s);
if (ret < 0) {
mslog(s, NULL, LOG_ERR, "error loading the certificate or key file\n");
exit(1);
}
if (s->config->cert_req != GNUTLS_CERT_IGNORE) {
@@ -519,40 +569,6 @@ const char* perr;
return;
}
int tls_global_init_client(worker_st* ws)
{
#ifdef HAVE_PKCS11
int ret;
unsigned i;
/* when we have PKCS #11 keys we cannot open them and then fork(), we need
* to open them at the process they are going to be used. */
for (i=0;i<ws->config->key_size;i++) {
if (strncmp(ws->config->key[i], "pkcs11:", 7) == 0) {
ret = gnutls_pkcs11_reinit();
if (ret < 0) {
oclog(ws, LOG_ERR, "could not reinitialize PKCS #11 subsystem: %s\n",
gnutls_strerror(ret));
return -1;
}
ret =
gnutls_certificate_set_x509_key_file(ws->creds->xcred, ws->config->cert[i],
ws->config->key[i],
GNUTLS_X509_FMT_PEM);
if (ret < 0) {
oclog(ws, LOG_ERR, "error setting the certificate (%s) or key (%s) files: %s\n",
ws->config->cert[i], ws->config->key[i], gnutls_strerror(ret));
return -1;
}
}
}
#endif
return 0;
}
void tls_cork(gnutls_session_t session)
{
#if GNUTLS_VERSION_NUMBER > 0x030109

View File

@@ -19,7 +19,6 @@ void tls_cork(gnutls_session_t session);
int tls_uncork(gnutls_session_t session);
void tls_global_init(struct main_server_st* s);
int tls_global_init_client(struct worker_st* ws);
void tls_global_init_certs(struct main_server_st* s);
ssize_t tls_send_file(gnutls_session_t session, const char *file);
@@ -51,15 +50,11 @@ void tls_close(gnutls_session_t session);
void tls_fatal_close(gnutls_session_t session,
gnutls_alert_description_t a);
#define MAX_PIN_SIZE GNUTLS_PKCS11_MAX_PIN_LEN
struct tls_st {
gnutls_certificate_credentials_t xcred;
gnutls_priority_t cprio;
gnutls_dh_params_t dh_params;
gnutls_datum_t ticket_key;
char pin[MAX_PIN_SIZE];
char srk_pin[MAX_PIN_SIZE];
};
typedef struct

View File

@@ -75,6 +75,8 @@ struct cfg_st {
char *chroot_dir; /* where the xml files are served from */
char *banner;
char *ocsp_response; /* file with the OCSP response */
char* socket_file_prefix;
time_t cookie_validity; /* in seconds */
time_t min_reauth_time; /* after a failed auth, how soon one can reauthenticate -> in seconds */
unsigned auth_timeout; /* timeout of HTTP auth */
@@ -104,7 +106,7 @@ struct cfg_st {
uid_t uid;
gid_t gid;
/* the tun network */
struct vpn_st network;
};

View File

@@ -191,25 +191,4 @@ fail:
return ret;
}
ssize_t tun_write(int sockfd, const void *buf, size_t len)
{
int left = len;
int ret;
const uint8_t * p = buf;
while(left > 0) {
ret = write(sockfd, p, left);
if (ret == -1) {
if (errno != EAGAIN && errno != EINTR)
return ret;
}
if (ret > 0) {
left -= ret;
p += ret;
}
}
return len;
}

View File

@@ -39,6 +39,7 @@
#include <arpa/inet.h>
#include <signal.h>
#include <time.h>
#include <common.h>
#include <vpn.h>
#include "ipc.h"
@@ -1217,7 +1218,7 @@ int ret, e, l;
break;
case AC_PKT_DATA:
oclog(ws, LOG_DEBUG, "writing %d byte(s) to TUN", (int)buf_size);
ret = tun_write(ws->tun_fd, buf, buf_size);
ret = force_write(ws->tun_fd, buf, buf_size);
if (ret == -1) {
e = errno;
oclog(ws, LOG_ERR, "could not write data to tun: %s", strerror(e));