Added cookie key rotation

This commit is contained in:
Nikos Mavrogiannopoulos
2015-11-14 10:41:22 +01:00
parent fd5f9df898
commit 2473633b8d
13 changed files with 120 additions and 4 deletions

View File

@@ -298,6 +298,13 @@ ban-reset-time = 300
# between different networks.
cookie-timeout = 300
# Cookie rekey time (in seconds)
# The time after which the key used to encrypt cookies will be
# refreshed. After this time the previous key will also be valid
# for verification. It is recommended not to modify the default
# value.
cookie-rekey-time = 14400
# If this is enabled (not recommended) the cookies will stay
# valid even after a user manually disconnects, and until they
# expire. This may improve roaming with some broken clients.

View File

@@ -85,6 +85,8 @@ static char tmp[32];
return "sm: ban IP";
case SM_CMD_AUTH_BAN_IP_REPLY:
return "sm: ban IP reply";
case SM_CMD_REFRESH_COOKIE_KEY:
return "sm: refresh cookie key";
default:
snprintf(tmp, sizeof(tmp), "unknown (%u)", _cmd);
return tmp;

View File

@@ -139,6 +139,7 @@ static struct cfg_options available_options[] = {
{ .name = "net-priority", .type = OPTION_STRING, .mandatory = 0 },
{ .name = "output-buffer", .type = OPTION_NUMERIC, .mandatory = 0 },
{ .name = "cookie-timeout", .type = OPTION_NUMERIC, .mandatory = 0 },
{ .name = "cookie-rekey-time", .type = OPTION_NUMERIC, .mandatory = 0 },
{ .name = "session-timeout", .type = OPTION_NUMERIC, .mandatory = 0 },
{ .name = "stats-report-time", .type = OPTION_NUMERIC, .mandatory = 0 },
{ .name = "rekey-time", .type = OPTION_NUMERIC, .mandatory = 0 },
@@ -871,6 +872,10 @@ size_t urlfw_size = 0;
config->cookie_timeout = DEFAULT_COOKIE_RECON_TIMEOUT;
READ_TF("persistent-cookies", config->persistent_cookies, 0);
READ_NUMERIC("cookie-rekey-time", config->cookie_rekey_time);
if (config->cookie_rekey_time == 0)
config->cookie_rekey_time = DEFAULT_COOKIE_REKEY_TIME;
READ_NUMERIC("session-timeout", config->session_timeout);
READ_NUMERIC("auth-timeout", config->auth_timeout);

View File

@@ -30,6 +30,7 @@
/* The time after a disconnection the cookie is valid */
#define DEFAULT_COOKIE_RECON_TIMEOUT 120
#define DEFAULT_COOKIE_REKEY_TIME 14400
int encrypt_cookie(void *pool, gnutls_datum_t *key, const Cookie *msg,
uint8_t** ecookie, unsigned *ecookie_size);

View File

@@ -264,5 +264,10 @@ message sec_auth_session_reply_msg
optional uint32 tunnel_all_dns = 34;
}
message sec_refresh_cookie_key
{
required bytes key = 1;
}
/* SEC_BAN_IP: sent from sec-mod to main */
/* same as: ban_ip_msg */

View File

@@ -220,6 +220,15 @@ struct proc_st *old_proc;
}
ret = decrypt_cookie(&pa, &key, req->cookie.data, req->cookie.len, &cmsg);
if (ret < 0 && s->prev_cookie_key_active) {
/* try the old key */
key.data = s->prev_cookie_key;
key.size = sizeof(s->prev_cookie_key);
ret = decrypt_cookie(&pa, &key, req->cookie.data, req->cookie.len, &cmsg);
if (ret == 0)
mslog(s, proc, LOG_INFO, "decrypted cookie with previous key");
}
if (ret < 0) {
mslog(s, proc, LOG_INFO, "error decrypting cookie");
return -1;

View File

@@ -60,6 +60,7 @@ int handle_sec_mod_commands(main_server_st * s)
void *pool = talloc_new(s);
PROTOBUF_ALLOCATOR(pa, pool);
BanIpMsg *tmsg = NULL;
SecRefreshCookieKey *rmsg = NULL;
if (pool == NULL)
return -1;
@@ -116,6 +117,29 @@ int handle_sec_mod_commands(main_server_st * s)
}
switch (cmd) {
case SM_CMD_REFRESH_COOKIE_KEY:
rmsg = sec_refresh_cookie_key__unpack(&pa, raw_len, raw);
if (rmsg == NULL) {
mslog(s, NULL, LOG_ERR, "error unpacking sec-mod data");
ret = ERR_BAD_COMMAND;
goto cleanup;
}
if (rmsg->key.len != sizeof(s->cookie_key)) {
mslog(s, NULL, LOG_ERR, "received corrupt cookie key (%u bytes) from sec-mod", (unsigned)rmsg->key.len);
ret = ERR_BAD_COMMAND;
goto cleanup;
}
memcpy(s->prev_cookie_key, s->cookie_key, sizeof(s->cookie_key));
s->prev_cookie_key_active = 1;
memcpy(s->cookie_key, rmsg->key.data, sizeof(s->cookie_key));
safe_memset(rmsg->key.data, 0, rmsg->key.len);
mslog(s, NULL, LOG_INFO, "refreshed cookie key");
break;
case SM_CMD_AUTH_BAN_IP:{
BanIpReplyMsg reply = BAN_IP_REPLY_MSG__INIT;

View File

@@ -1219,6 +1219,7 @@ int main(int argc, char** argv)
/* clear the cookie key */
safe_memset(s->cookie_key, 0, sizeof(s->cookie_key));
safe_memset(s->prev_cookie_key, 0, sizeof(s->prev_cookie_key));
setproctitle(PACKAGE_NAME"-worker");
kill_on_parent_kill(SIGTERM);

View File

@@ -179,6 +179,9 @@ typedef struct main_server_st {
tls_st *creds;
uint8_t cookie_key[COOKIE_KEY_SIZE];
/* when we rotate keys, key the previous one active for verification */
uint8_t prev_cookie_key[COOKIE_KEY_SIZE];
unsigned prev_cookie_key_active;
struct listen_list_st listen_list;
struct proc_list_st proc_list;

View File

@@ -385,6 +385,13 @@ ban-reset-time = 300
# between different networks.
cookie-timeout = 300
# Cookie rekey time (in seconds)
# The time after which the key used to encrypt cookies will be
# refreshed. After this time the previous key will also be valid
# for verification. It is recommended not to modify the default
# value.
cookie-rekey-time = 14400
# If this is enabled (not recommended) the cookies will stay
# valid even after a user manually disconnects, and until they
# expire. This may improve roaming with some broken clients.

View File

@@ -1,5 +1,5 @@
/*
* Copyright (C) 2013, 2014 Nikos Mavrogiannopoulos
* Copyright (C) 2013, 2014, 2015 Nikos Mavrogiannopoulos
*
* This file is part of ocserv.
*
@@ -44,6 +44,7 @@
#include <cloexec.h>
#include <gnutls/gnutls.h>
#include <gnutls/crypto.h>
#include <gnutls/abstract.h>
#define MAX_WAIT_SECS 3
@@ -176,6 +177,24 @@ int load_pins(struct perm_cfg_st *config, struct pin_st *s)
return 0;
}
static int send_refresh_cookie_key(sec_mod_st * sec, void *key_data, unsigned key_size)
{
SecRefreshCookieKey msg = SEC_REFRESH_COOKIE_KEY__INIT;
int ret;
msg.key.data = key_data;
msg.key.len = key_size;
ret = send_msg(sec, sec->cmd_fd, SM_CMD_REFRESH_COOKIE_KEY, &msg,
(pack_size_func) sec_refresh_cookie_key__get_packed_size,
(pack_func) sec_refresh_cookie_key__pack);
if (ret < 0) {
seclog(sec, LOG_WARNING, "sec-mod error in sending cookie key");
}
return 0;
}
static int handle_op(void *pool, int cfd, sec_mod_st * sec, uint8_t type, uint8_t * rep,
size_t rep_size)
{
@@ -383,6 +402,8 @@ static void handle_sigterm(int signo)
static void check_other_work(sec_mod_st *sec)
{
time_t now = time(0);
if (need_exit) {
unsigned i;
@@ -395,6 +416,23 @@ static void check_other_work(sec_mod_st *sec)
exit(0);
}
if (sec->config->cookie_rekey_time > 0 && now - sec->cookie_key_last_update > sec->config->cookie_rekey_time) {
uint8_t cookie_key[COOKIE_KEY_SIZE];
int ret;
ret = gnutls_rnd(GNUTLS_RND_RANDOM, cookie_key, sizeof(cookie_key));
if (ret >= 0) {
if (send_refresh_cookie_key(sec, cookie_key, sizeof(cookie_key)) == 0) {
sec->cookie_key_last_update = now;
memcpy(sec->cookie_key, cookie_key, sizeof(cookie_key));
} else {
seclog(sec, LOG_ERR, "could not notify main for new cookie key");
}
} else {
seclog(sec, LOG_ERR, "could not refresh cookie key");
}
}
if (need_reload) {
seclog(sec, LOG_DEBUG, "reloading configuration");
reload_cfg_file(sec, sec->perm_config);
@@ -560,6 +598,11 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char
void *sec_mod_pool;
fd_set rd_set;
pid_t pid;
#ifdef HAVE_PSELECT
struct timespec ts;
#else
struct timeval ts;
#endif
sigset_t emptyset, blockset;
#ifdef DEBUG_LEAKS
@@ -585,6 +628,8 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char
}
memcpy(sec->cookie_key, cookie_key, COOKIE_KEY_SIZE);
sec->cookie_key_last_update = time(0);
sec->dcookie_key.data = sec->cookie_key;
sec->dcookie_key.size = COOKIE_KEY_SIZE;
sec->perm_config = talloc_steal(sec, perm_config);
@@ -730,13 +775,17 @@ void sec_mod_server(void *main_pool, struct perm_cfg_st *perm_config, const char
n = MAX(n, sd);
#ifdef HAVE_PSELECT
ret = pselect(n + 1, &rd_set, NULL, NULL, NULL, &emptyset);
ts.tv_nsec = 0;
ts.tv_sec = 120;
ret = pselect(n + 1, &rd_set, NULL, NULL, &ts, &emptyset);
#else
ts.tv_usec = 0;
ts.tv_sec = 120;
sigprocmask(SIG_UNBLOCK, &blockset, NULL);
ret = select(n + 1, &rd_set, NULL, NULL, NULL);
ret = select(n + 1, &rd_set, NULL, NULL, &ts);
sigprocmask(SIG_BLOCK, &blockset, NULL);
#endif
if (ret == -1 && errno == EINTR)
if (ret == 0 || (ret == -1 && errno == EINTR))
continue;
if (ret < 0) {

View File

@@ -31,6 +31,7 @@
typedef struct sec_mod_st {
gnutls_datum_t dcookie_key; /* the key to generate cookies */
uint8_t cookie_key[COOKIE_KEY_SIZE];
time_t cookie_key_last_update;
struct cfg_st *config;
struct perm_cfg_st *perm_config;

View File

@@ -176,6 +176,7 @@ typedef enum {
SM_CMD_AUTH_BAN_IP,
SM_CMD_AUTH_BAN_IP_REPLY,
SM_CMD_AUTH_CLI_STATS,
SM_CMD_REFRESH_COOKIE_KEY,
MAX_SM_MAIN_CMD,
} cmd_request_t;
@@ -329,6 +330,7 @@ struct cfg_st {
unsigned deny_roaming; /* whether a cookie is restricted to a single IP */
time_t cookie_timeout; /* in seconds */
time_t cookie_rekey_time; /* in seconds */
time_t session_timeout; /* in seconds */
unsigned persistent_cookies; /* whether cookies stay valid after disconnect */