mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 08:46:58 +08:00
Added cookie key rotation
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user