mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 16:57:00 +08:00
Prevent tampering of our_ip, ip, session_start_time in SEC_AUTH_INIT from ocserv-worker to ocserv->sm and reject replay of auth_init_messages from old sessions.
Resolves: #252 Signed-off-by: Alan Jowett <alanjo@microsoft.com>
This commit is contained in:
@@ -45,7 +45,8 @@ ocserv_SOURCES = main.c main-auth.c worker-vpn.c worker-auth.c tlslib.c \
|
||||
main-ban.c main-ban.h common-config.h valid-hostname.c \
|
||||
str.c str.h gettime.h $(CCAN_SOURCES) $(HTTP_PARSER_SOURCES) \
|
||||
sec-mod-acct.h setproctitle.c setproctitle.h sec-mod-resume.h \
|
||||
sec-mod-cookies.c defs.h inih/ini.c inih/ini.h
|
||||
sec-mod-cookies.c defs.h inih/ini.c inih/ini.h \
|
||||
common/hmac.h common/hmac.c
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -36,6 +36,7 @@ void *_talloc_size2(void *ctx, size_t size);
|
||||
|
||||
#define MAX_IP_STR 46
|
||||
|
||||
|
||||
#define PROTOBUF_ALLOCATOR(name, pool) \
|
||||
ProtobufCAllocator name = {.alloc = _talloc_size2, .free = _talloc_free2, .allocator_data = pool}
|
||||
|
||||
|
||||
58
src/common/hmac.c
Normal file
58
src/common/hmac.c
Normal file
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Microsoft Corporation
|
||||
*
|
||||
* Author: Alan Jowett
|
||||
*
|
||||
* This file is part of ocserv.
|
||||
*
|
||||
* The GnuTLS is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
#include <config.h>
|
||||
#include <string.h>
|
||||
|
||||
#include <gnutls/gnutls.h>
|
||||
#include <gnutls/crypto.h>
|
||||
#include <gnutls/abstract.h>
|
||||
#include <nettle/hmac.h>
|
||||
#include <common.h>
|
||||
#include <hmac.h>
|
||||
#include <stdbool.h>
|
||||
|
||||
bool hmac_init_key(size_t key_length, uint8_t * key)
|
||||
{
|
||||
return gnutls_rnd(GNUTLS_RND_RANDOM, key, key_length) == 0;
|
||||
}
|
||||
|
||||
void generate_hmac(size_t key_length, const uint8_t * key, size_t component_count,
|
||||
const hmac_component_st * components, uint8_t digest[HMAC_DIGEST_SIZE])
|
||||
{
|
||||
struct hmac_sha256_ctx ctx;
|
||||
size_t i;
|
||||
|
||||
hmac_sha256_set_key(&ctx, key_length, key);
|
||||
|
||||
for (i = 0; i < component_count; i++) {
|
||||
|
||||
if (components[i].data) {
|
||||
hmac_sha256_update(&ctx,
|
||||
components[i].length,
|
||||
(const uint8_t *)components[i].data);
|
||||
}
|
||||
}
|
||||
|
||||
hmac_sha256_digest(&ctx, HMAC_DIGEST_SIZE, digest);
|
||||
|
||||
safe_memset(&ctx, 0, sizeof(ctx));
|
||||
}
|
||||
39
src/common/hmac.h
Normal file
39
src/common/hmac.h
Normal file
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright (C) 2020 Microsoft Corporation
|
||||
*
|
||||
* Author: Alan Jowett
|
||||
*
|
||||
* This file is part of ocserv.
|
||||
*
|
||||
* The GnuTLS is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU Lesser General Public License
|
||||
* as published by the Free Software Foundation; either version 2.1 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This library 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
|
||||
* Lesser General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU Lesser General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>
|
||||
*/
|
||||
|
||||
|
||||
#ifndef HMAC_H
|
||||
#define HMAC_H
|
||||
#include <stdbool.h>
|
||||
|
||||
#define HMAC_DIGEST_SIZE 32
|
||||
|
||||
bool hmac_init_key(size_t key_length, uint8_t * key);
|
||||
|
||||
typedef struct hmac_component_st {
|
||||
size_t length;
|
||||
void * data;
|
||||
} hmac_component_st;
|
||||
|
||||
void generate_hmac(size_t key_length, const uint8_t * key, size_t component_count,
|
||||
const hmac_component_st * components, uint8_t digest[HMAC_DIGEST_SIZE]);
|
||||
|
||||
#endif
|
||||
@@ -215,6 +215,8 @@ message sec_auth_init_msg
|
||||
optional string our_ip = 10;
|
||||
optional string user_agent = 11;
|
||||
optional string vhost = 12;
|
||||
required uint64 session_start_time = 13;
|
||||
required bytes hmac = 14;
|
||||
}
|
||||
|
||||
/* SEC_AUTH_CONT */
|
||||
|
||||
@@ -787,7 +787,7 @@ int run_sec_mod(main_server_st *s, int *sync_fd)
|
||||
set_cloexec_flag (fd[0], 1);
|
||||
set_cloexec_flag (sfd[0], 1);
|
||||
clear_unneeded_mem(s->vconfig);
|
||||
sec_mod_server(s->main_pool, s->config_pool, s->vconfig, p, fd[0], sfd[0]);
|
||||
sec_mod_server(s->main_pool, s->config_pool, s->vconfig, p, fd[0], sfd[0], sizeof(s->hmac_key), s->hmac_key);
|
||||
exit(0);
|
||||
} else if (pid > 0) { /* parent */
|
||||
close(fd[0]);
|
||||
|
||||
23
src/main.c
23
src/main.c
@@ -62,6 +62,7 @@
|
||||
#include <grp.h>
|
||||
#include <ip-lease.h>
|
||||
#include <ccan/list/list.h>
|
||||
#include <hmac.h>
|
||||
|
||||
#ifdef HAVE_GSSAPI
|
||||
# include <libtasn1.h>
|
||||
@@ -1042,6 +1043,7 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents)
|
||||
int fd, ret;
|
||||
int cmd_fd[2];
|
||||
pid_t pid;
|
||||
hmac_component_st hmac_components[3];
|
||||
|
||||
if (ltmp->sock_type == SOCK_TYPE_TCP || ltmp->sock_type == SOCK_TYPE_UNIX) {
|
||||
/* connection on TCP port */
|
||||
@@ -1120,6 +1122,22 @@ static void listen_watcher_cb (EV_P_ ev_io *w, int revents)
|
||||
ws->dtls_tptr.fd = -1;
|
||||
ws->conn_fd = fd;
|
||||
ws->conn_type = stype;
|
||||
ws->session_start_time = time(0);
|
||||
|
||||
human_addr2((const struct sockaddr *)&ws->remote_addr, ws->remote_addr_len, ws->remote_ip_str, sizeof(ws->remote_ip_str), 0);
|
||||
human_addr2((const struct sockaddr *)&ws->our_addr, ws->our_addr_len, ws->our_ip_str, sizeof(ws->our_ip_str), 0);
|
||||
|
||||
hmac_components[0].data = ws->remote_ip_str;
|
||||
hmac_components[0].length = ws->remote_ip_str ? strlen(ws->remote_ip_str) : 0;
|
||||
hmac_components[1].data = ws->our_ip_str;
|
||||
hmac_components[1].length = ws->our_ip_str ? strlen(ws->our_ip_str) : 0;
|
||||
hmac_components[2].data = &ws->session_start_time;
|
||||
hmac_components[2].length = sizeof(ws->session_start_time);
|
||||
|
||||
generate_hmac(sizeof(s->hmac_key), s->hmac_key, sizeof(hmac_components) / sizeof(hmac_components[0]), hmac_components, (uint8_t*) ws->sec_auth_init_hmac);
|
||||
|
||||
// Clear the HMAC key
|
||||
safe_memset((uint8_t*)s->hmac_key, 0, sizeof(s->hmac_key));
|
||||
|
||||
/* Drop privileges after this point */
|
||||
drop_privileges(s);
|
||||
@@ -1268,6 +1286,11 @@ int main(int argc, char** argv)
|
||||
s->top_fd = -1;
|
||||
s->ctl_fd = -1;
|
||||
|
||||
if (!hmac_init_key(sizeof(s->hmac_key), (uint8_t*)(s->hmac_key))) {
|
||||
fprintf(stderr, "unable to generate hmac key\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
list_head_init(&s->proc_list.head);
|
||||
list_head_init(&s->script_list.head);
|
||||
ip_lease_init(&s->ip_leases);
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
#include <sys/uio.h>
|
||||
#include <signal.h>
|
||||
#include <ev.h>
|
||||
|
||||
#include <hmac.h>
|
||||
#include "vhost.h"
|
||||
|
||||
#if defined(__FreeBSD__) || defined(__OpenBSD__)
|
||||
@@ -255,6 +255,8 @@ typedef struct main_server_st {
|
||||
void *main_pool; /* talloc main pool */
|
||||
void *config_pool; /* talloc config pool */
|
||||
|
||||
const uint8_t hmac_key[HMAC_DIGEST_SIZE];
|
||||
|
||||
/* used as temporary buffer (currently by forward_udp_to_owner) */
|
||||
uint8_t msg_buffer[MAX_MSG_SIZE];
|
||||
} main_server_st;
|
||||
|
||||
@@ -53,6 +53,7 @@
|
||||
#include <sec-mod-sup-config.h>
|
||||
#include <sec-mod-acct.h>
|
||||
#include <c-strcase.h>
|
||||
#include <hmac.h>
|
||||
|
||||
#ifdef HAVE_GSSAPI
|
||||
# include <gssapi/gssapi.h>
|
||||
@@ -768,9 +769,37 @@ int handle_sec_auth_init(int cfd, sec_mod_st *sec, const SecAuthInitMsg *req, pi
|
||||
unsigned i;
|
||||
unsigned need_continue = 0;
|
||||
vhost_cfg_st *vhost;
|
||||
hmac_component_st hmac_components[3];
|
||||
uint8_t computed_hmac[HMAC_DIGEST_SIZE];
|
||||
time_t now = time(0);
|
||||
|
||||
if (req->hmac.len != HMAC_DIGEST_SIZE || !req->hmac.data) {
|
||||
seclog(sec, LOG_AUTH, "hmac is the wrong size");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Authenticate the client parameters */
|
||||
hmac_components[0].data = req->ip;
|
||||
hmac_components[0].length = req->ip ? strlen(req->ip) : 0;
|
||||
hmac_components[1].data = req->our_ip;
|
||||
hmac_components[1].length = req->our_ip ? strlen(req->our_ip) : 0;
|
||||
hmac_components[2].data = (void*)&req->session_start_time;
|
||||
hmac_components[2].length = sizeof(req->session_start_time);
|
||||
|
||||
generate_hmac(sizeof(sec->hmac_key), sec->hmac_key, sizeof(hmac_components) / sizeof(hmac_components[0]), hmac_components, computed_hmac);
|
||||
|
||||
if (memcmp(computed_hmac, req->hmac.data, req->hmac.len) != 0) {
|
||||
seclog(sec, LOG_AUTH, "hmac presented by client doesn't match parameters provided - possible replay");
|
||||
return -1;
|
||||
}
|
||||
|
||||
vhost = find_vhost(sec->vconfig, req->vhost);
|
||||
|
||||
if ((now - req->session_start_time) > vhost->perm_config.config->auth_timeout) {
|
||||
seclog(sec, LOG_AUTH, "hmac presented by client expired - possible replay");
|
||||
return -1;
|
||||
}
|
||||
|
||||
e = new_client_entry(sec, vhost, req->ip, pid);
|
||||
if (e == NULL) {
|
||||
seclog(sec, LOG_ERR, "cannot initialize memory");
|
||||
|
||||
@@ -887,7 +887,8 @@ static int load_keys(sec_mod_st *sec, unsigned force)
|
||||
* key operations.
|
||||
*/
|
||||
void sec_mod_server(void *main_pool, void *config_pool, struct list_head *vconfig,
|
||||
const char *socket_file, int cmd_fd, int cmd_fd_sync)
|
||||
const char *socket_file, int cmd_fd, int cmd_fd_sync,
|
||||
size_t hmac_key_length, const uint8_t * hmac_key)
|
||||
{
|
||||
struct sockaddr_un sa;
|
||||
socklen_t sa_len;
|
||||
@@ -933,6 +934,7 @@ void sec_mod_server(void *main_pool, void *config_pool, struct list_head *vconfi
|
||||
sec->vconfig = vconfig;
|
||||
sec->config_pool = config_pool;
|
||||
sec->sec_mod_pool = sec_mod_pool;
|
||||
memcpy((uint8_t*)sec->hmac_key, hmac_key, hmac_key_length);
|
||||
|
||||
tls_cache_init(sec, &sec->tls_db);
|
||||
sup_config_init(sec);
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
#include <ccan/htable/htable.h>
|
||||
#include <nettle/base64.h>
|
||||
#include <tlslib.h>
|
||||
#include <hmac.h>
|
||||
#include "common/common.h"
|
||||
|
||||
#include "vhost.h"
|
||||
@@ -47,6 +48,7 @@ typedef struct sec_mod_st {
|
||||
uint32_t avg_auth_time; /* the average time spent in (sucessful) authentication */
|
||||
uint32_t total_authentications; /* successful authentications: to calculate the average above */
|
||||
time_t last_stats_reset;
|
||||
const uint8_t hmac_key[HMAC_DIGEST_SIZE];
|
||||
} sec_mod_st;
|
||||
|
||||
typedef struct stats_st {
|
||||
@@ -164,6 +166,7 @@ void sec_auth_user_deinit(sec_mod_st *sec, client_entry_st *e);
|
||||
|
||||
void sec_mod_server(void *main_pool, void *config_pool, struct list_head *vconfig,
|
||||
const char *socket_file,
|
||||
int cmd_fd, int cmd_fd_sync);
|
||||
int cmd_fd, int cmd_fd_sync,
|
||||
size_t hmac_key_length, const uint8_t * hmac_key);
|
||||
|
||||
#endif
|
||||
|
||||
@@ -1330,27 +1330,6 @@ int basic_auth_handler(worker_st * ws, unsigned http_ver, const char *msg)
|
||||
return ret;
|
||||
}
|
||||
|
||||
static char *get_our_ip(worker_st * ws, char str[MAX_IP_STR])
|
||||
{
|
||||
int ret;
|
||||
struct sockaddr_storage sockaddr;
|
||||
socklen_t socklen;
|
||||
|
||||
if (ws->our_addr_len > 0) {
|
||||
return human_addr2((struct sockaddr*)&ws->our_addr, ws->our_addr_len, str, MAX_IP_STR, 0);
|
||||
}
|
||||
|
||||
if (ws->udp_state != UP_ACTIVE)
|
||||
return NULL;
|
||||
|
||||
socklen = sizeof(sockaddr);
|
||||
ret = getsockname(ws->dtls_tptr.fd, (struct sockaddr*)&sockaddr, &socklen);
|
||||
if (ret == -1)
|
||||
return NULL;
|
||||
|
||||
return human_addr2((struct sockaddr*)&sockaddr, socklen, str, MAX_IP_STR, 0);
|
||||
}
|
||||
|
||||
#define USERNAME_FIELD "username"
|
||||
#define GROUPNAME_FIELD "group%5flist"
|
||||
#define GROUPNAME_FIELD2 "group_list"
|
||||
@@ -1369,7 +1348,6 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
char *username = NULL;
|
||||
char *password = NULL;
|
||||
char *groupname = NULL;
|
||||
char our_ip_str[MAX_IP_STR];
|
||||
char *msg = NULL;
|
||||
unsigned def_group = 0;
|
||||
unsigned pcounter = 0;
|
||||
@@ -1475,7 +1453,10 @@ int post_auth_handler(worker_st * ws, unsigned http_ver)
|
||||
|
||||
ireq.vhost = ws->vhost->name;
|
||||
ireq.ip = ws->remote_ip_str;
|
||||
ireq.our_ip = get_our_ip(ws, our_ip_str);
|
||||
ireq.our_ip = ws->our_ip_str;
|
||||
ireq.session_start_time = ws->session_start_time;
|
||||
ireq.hmac.data = (uint8_t*)ws->sec_auth_init_hmac;
|
||||
ireq.hmac.len = sizeof(ws->sec_auth_init_hmac);
|
||||
if (req->user_agent[0] != 0)
|
||||
ireq.user_agent = req->user_agent;
|
||||
|
||||
|
||||
@@ -736,7 +736,6 @@ void vpn_server(struct worker_st *ws)
|
||||
"could not disable system calls, kernel might not support seccomp");
|
||||
}
|
||||
}
|
||||
ws->session_start_time = time(0);
|
||||
|
||||
if (ws->remote_addr_len == sizeof(struct sockaddr_in))
|
||||
ws->proto = AF_INET;
|
||||
@@ -819,8 +818,6 @@ void vpn_server(struct worker_st *ws)
|
||||
settings.on_body = http_body_cb;
|
||||
http_req_init(ws);
|
||||
|
||||
human_addr2((void*)&ws->remote_addr, ws->remote_addr_len, ws->remote_ip_str, sizeof(ws->remote_ip_str), 0);
|
||||
|
||||
if (WSCONFIG(ws)->listen_proxy_proto) {
|
||||
oclog(ws, LOG_DEBUG, "proxy-hdr: peer is %s\n", ws->remote_ip_str);
|
||||
}
|
||||
|
||||
@@ -36,6 +36,7 @@
|
||||
#include <stdbool.h>
|
||||
#include <sys/un.h>
|
||||
#include <sys/uio.h>
|
||||
#include <hmac.h>
|
||||
#include "vhost.h"
|
||||
|
||||
typedef enum {
|
||||
@@ -201,7 +202,9 @@ typedef struct worker_st {
|
||||
socklen_t our_addr_len;
|
||||
struct sockaddr_storage remote_addr; /* peer's address */
|
||||
socklen_t remote_addr_len;
|
||||
char our_ip_str[MAX_IP_STR];
|
||||
char remote_ip_str[MAX_IP_STR];
|
||||
const uint8_t sec_auth_init_hmac[HMAC_DIGEST_SIZE];
|
||||
|
||||
int proto; /* AF_INET or AF_INET6 */
|
||||
|
||||
|
||||
Reference in New Issue
Block a user