Merge branch 'issue252' into 'master'

Use an HMAC to verify the local and remote IP presented during SEC_AUTH_INIT phase.

Closes #252

See merge request openconnect/ocserv!139
This commit is contained in:
Nikos Mavrogiannopoulos
2020-02-28 20:16:47 +00:00
14 changed files with 172 additions and 31 deletions

View File

@@ -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

View File

@@ -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
View 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
View 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

View File

@@ -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 */

View File

@@ -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]);

View File

@@ -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);

View File

@@ -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;

View File

@@ -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");

View File

@@ -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);

View File

@@ -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

View File

@@ -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;

View File

@@ -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);
}

View File

@@ -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 */