mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-09 16:26:59 +08:00
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:
1
.gitignore
vendored
1
.gitignore
vendored
@@ -34,3 +34,4 @@ gl/stdlib.h
|
||||
gl/sys/stat.h
|
||||
gl/sys/time.h
|
||||
gl/sys/types.h
|
||||
src/ocpasswd
|
||||
|
||||
5
NEWS
5
NEWS
@@ -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
9
TODO
@@ -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,
|
||||
|
||||
@@ -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])
|
||||
])
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
BIN
doc/design.dia
BIN
doc/design.dia
Binary file not shown.
@@ -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
1
doc/sample.passwd
Normal file
@@ -0,0 +1 @@
|
||||
test:tost:$5$i6SNmLDCgBNjyJ7q$SZ4bVJb7I/DLgXo3txHBVohRFBjOtdbxGQZp.DOnrA.
|
||||
@@ -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
44
src/common.c
Normal 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
6
src/common.h
Normal file
@@ -0,0 +1,6 @@
|
||||
#ifndef COMMON_H
|
||||
# define COMMON_H
|
||||
ssize_t force_write(int sockfd, const void *buf, size_t len);
|
||||
|
||||
#endif
|
||||
|
||||
11
src/config.c
11
src/config.c
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
24
src/main.c
24
src/main.c
@@ -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);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
*
|
||||
|
||||
@@ -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_;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
264
src/sec-mod.c
Normal 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
5
src/sec-mod.h
Normal file
@@ -0,0 +1,5 @@
|
||||
#ifndef SEC_MOD_H
|
||||
|
||||
void sec_mod_server(struct cfg_st* config, int sd);
|
||||
|
||||
#endif
|
||||
326
src/tlslib.c
326
src/tlslib.c
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
Reference in New Issue
Block a user