mirror of
https://gitlab.com/openconnect/ocserv.git
synced 2026-02-10 08:46:58 +08:00
321 lines
8.5 KiB
C
321 lines
8.5 KiB
C
/*
|
|
* Copyright (C) 2014 Red Hat
|
|
*
|
|
* 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, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <config.h>
|
|
|
|
#include <stdio.h>
|
|
|
|
#include <proc-search.h>
|
|
#include <main.h>
|
|
#include <common.h>
|
|
|
|
struct find_ip_st {
|
|
struct sockaddr_storage *sockaddr;
|
|
unsigned sockaddr_size;
|
|
unsigned found_ips;
|
|
};
|
|
|
|
struct find_dtls_id_st {
|
|
const uint8_t *dtls_id;
|
|
unsigned dtls_id_size;
|
|
};
|
|
|
|
struct find_sid_st {
|
|
const uint8_t *sid;
|
|
};
|
|
|
|
|
|
static size_t rehash_ip(const void* _p, void* unused)
|
|
{
|
|
const struct proc_st * proc = _p;
|
|
|
|
return hash_any(
|
|
SA_IN_P_GENERIC(&proc->remote_addr, proc->remote_addr_len),
|
|
SA_IN_SIZE(proc->remote_addr_len), 0);
|
|
}
|
|
|
|
static size_t rehash_dtls_ip(const void* _p, void* unused)
|
|
{
|
|
const struct proc_st * proc = _p;
|
|
|
|
return hash_any(
|
|
SA_IN_P_GENERIC(&proc->dtls_remote_addr, proc->dtls_remote_addr_len),
|
|
SA_IN_SIZE(proc->dtls_remote_addr_len), 0);
|
|
}
|
|
|
|
static size_t rehash_dtls_id(const void* _p, void* unused)
|
|
{
|
|
const struct proc_st * proc = _p;
|
|
|
|
return hash_any(proc->dtls_session_id, proc->dtls_session_id_size, 0);
|
|
}
|
|
|
|
static size_t rehash_sid(const void* _p, void* unused)
|
|
{
|
|
const struct proc_st * proc = _p;
|
|
|
|
return hash_any(proc->sid, sizeof(proc->sid), 0);
|
|
}
|
|
|
|
void proc_table_init(main_server_st *s)
|
|
{
|
|
s->proc_table.db_ip = talloc(s, struct htable);
|
|
s->proc_table.db_dtls_ip = talloc(s, struct htable);
|
|
s->proc_table.db_dtls_id = talloc(s, struct htable);
|
|
s->proc_table.db_sid = talloc(s, struct htable);
|
|
htable_init(s->proc_table.db_ip, rehash_ip, NULL);
|
|
htable_init(s->proc_table.db_dtls_ip, rehash_dtls_ip, NULL);
|
|
htable_init(s->proc_table.db_dtls_id, rehash_dtls_id, NULL);
|
|
htable_init(s->proc_table.db_sid, rehash_sid, NULL);
|
|
s->proc_table.total = 0;
|
|
}
|
|
|
|
void proc_table_deinit(main_server_st *s)
|
|
{
|
|
htable_clear(s->proc_table.db_ip);
|
|
htable_clear(s->proc_table.db_dtls_ip);
|
|
htable_clear(s->proc_table.db_dtls_id);
|
|
htable_clear(s->proc_table.db_sid);
|
|
talloc_free(s->proc_table.db_ip);
|
|
talloc_free(s->proc_table.db_dtls_ip);
|
|
talloc_free(s->proc_table.db_dtls_id);
|
|
talloc_free(s->proc_table.db_sid);
|
|
}
|
|
|
|
/* Adds the IP of the CSTP channel into the IPs hash table and
|
|
* the session ID into the IDs hash table.
|
|
*/
|
|
int proc_table_add(main_server_st *s, struct proc_st *proc)
|
|
{
|
|
size_t ip_hash = rehash_ip(proc, NULL);
|
|
size_t dtls_id_hash = rehash_dtls_id(proc, NULL);
|
|
|
|
if (htable_add(s->proc_table.db_ip, ip_hash, proc) == 0) {
|
|
return -1;
|
|
}
|
|
|
|
if (htable_add(s->proc_table.db_dtls_id, dtls_id_hash, proc) == 0) {
|
|
htable_del(s->proc_table.db_ip, ip_hash, proc);
|
|
return -1;
|
|
}
|
|
|
|
if (htable_add(s->proc_table.db_sid, rehash_sid(proc, NULL), proc) == 0) {
|
|
htable_del(s->proc_table.db_ip, ip_hash, proc);
|
|
htable_del(s->proc_table.db_dtls_id, dtls_id_hash, proc);
|
|
return -1;
|
|
}
|
|
|
|
s->proc_table.total++;
|
|
|
|
return 0;
|
|
}
|
|
|
|
int proc_table_update_ip(main_server_st *s, struct proc_st *proc, struct sockaddr_storage *addr,
|
|
unsigned addr_size)
|
|
{
|
|
char buf[MAX_IP_STR];
|
|
unsigned removed;
|
|
|
|
/* only update if we can remove the old IP */
|
|
if (addr_size != proc->remote_addr_len ||
|
|
memcmp(addr, &proc->remote_addr, addr_size) != 0) {
|
|
mslog(s, proc, LOG_INFO, "updating remote IP to %s",
|
|
human_addr2((struct sockaddr*)addr, addr_size, buf, sizeof(buf), 0));
|
|
|
|
removed = htable_del(s->proc_table.db_ip, rehash_ip(proc, NULL), proc);
|
|
|
|
memcpy(&proc->remote_addr, addr, addr_size);
|
|
proc->remote_addr_len = addr_size;
|
|
|
|
/* If DTLS IP matches the CSTP IP remove it, to avoid
|
|
* keeping duplicate entries. */
|
|
if (proc->dtls_remote_addr_len == addr_size &&
|
|
memcmp(SA_IN_P_GENERIC(addr, addr_size),
|
|
SA_IN_P_GENERIC(&proc->dtls_remote_addr, addr_size),
|
|
SA_IN_SIZE(addr_size)) == 0) {
|
|
|
|
if (htable_del(s->proc_table.db_dtls_ip, rehash_dtls_ip(proc, NULL), proc) != 0)
|
|
proc->dtls_remote_addr_len = 0;
|
|
}
|
|
|
|
/* only add into table if the previous entry was there. That is because
|
|
* we may be called even before that entry is added, and we don't want
|
|
* duplicates */
|
|
if (removed) {
|
|
if (htable_add(s->proc_table.db_ip, rehash_ip(proc, NULL), proc) == 0)
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* Adds the IP of the DTLS channel into the DTLS IP hash table. It
|
|
* only adds the IP if it is different than the CSTP channel IP.
|
|
*/
|
|
int proc_table_update_dtls_ip(main_server_st *s, struct proc_st *proc, struct sockaddr_storage *addr, unsigned addr_size)
|
|
{
|
|
if (proc->dtls_remote_addr_len) {
|
|
if (proc->dtls_remote_addr_len == addr_size &&
|
|
memcmp(SA_IN_P_GENERIC(&proc->dtls_remote_addr, addr_size),
|
|
SA_IN_P_GENERIC(addr, addr_size),
|
|
SA_IN_SIZE(addr_size)) == 0) {
|
|
return -1; /* DTLS address is already up to date */
|
|
}
|
|
htable_del(s->proc_table.db_dtls_ip, rehash_dtls_ip(proc, NULL), proc);
|
|
}
|
|
|
|
proc->dtls_remote_addr_len = 0;
|
|
if (addr_size == 0)
|
|
return 0;
|
|
|
|
if (proc->remote_addr_len == addr_size &&
|
|
memcmp(SA_IN_P_GENERIC(&proc->remote_addr, addr_size),
|
|
SA_IN_P_GENERIC(addr, addr_size),
|
|
SA_IN_SIZE(addr_size)) == 0) {
|
|
return -1; /* CSTP and DTLS peer addresses match; do nothing */
|
|
}
|
|
|
|
proc->dtls_remote_addr_len = addr_size;
|
|
memcpy(&proc->dtls_remote_addr, addr, addr_size);
|
|
|
|
if (htable_add(s->proc_table.db_dtls_ip, rehash_dtls_ip(proc, NULL), proc) == 0)
|
|
return -1;
|
|
|
|
return 0;
|
|
}
|
|
|
|
void proc_table_del(main_server_st *s, struct proc_st *proc)
|
|
{
|
|
if (proc->dtls_remote_addr_len > 0)
|
|
htable_del(s->proc_table.db_dtls_ip, rehash_dtls_ip(proc, NULL), proc);
|
|
|
|
htable_del(s->proc_table.db_ip, rehash_ip(proc, NULL), proc);
|
|
htable_del(s->proc_table.db_dtls_id, rehash_dtls_id(proc, NULL), proc);
|
|
htable_del(s->proc_table.db_sid, rehash_sid(proc, NULL), proc);
|
|
}
|
|
|
|
static bool local_ip_cmp(const void* _c1, void* _c2)
|
|
{
|
|
const struct proc_st* c1 = _c1;
|
|
struct find_ip_st* c2 = _c2;
|
|
|
|
if (c2->sockaddr_size == 0)
|
|
return 0;
|
|
|
|
/* Test if peer IP matches DTLS IP */
|
|
if (c1->dtls_remote_addr_len == c2->sockaddr_size &&
|
|
memcmp(SA_IN_P_GENERIC(&c1->dtls_remote_addr, c1->dtls_remote_addr_len),
|
|
SA_IN_P_GENERIC(c2->sockaddr, c2->sockaddr_size),
|
|
SA_IN_SIZE(c1->dtls_remote_addr_len)) == 0) {
|
|
c2->found_ips++;
|
|
return 1;
|
|
}
|
|
|
|
/* Test if peer IP matches CSTP IP */
|
|
if (c1->remote_addr_len == c2->sockaddr_size &&
|
|
memcmp(SA_IN_P_GENERIC(&c1->remote_addr, c1->remote_addr_len),
|
|
SA_IN_P_GENERIC(c2->sockaddr, c2->sockaddr_size),
|
|
SA_IN_SIZE(c1->remote_addr_len)) == 0) {
|
|
c2->found_ips++;
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
/* returns a result only if there is a single IP matching
|
|
*/
|
|
struct proc_st *proc_search_single_ip(struct main_server_st *s,
|
|
struct sockaddr_storage *sockaddr,
|
|
unsigned sockaddr_size)
|
|
{
|
|
struct proc_st *proc;
|
|
struct find_ip_st fip;
|
|
size_t h;
|
|
|
|
fip.sockaddr = sockaddr;
|
|
fip.sockaddr_size = sockaddr_size;
|
|
|
|
h = hash_any(SA_IN_P_GENERIC(sockaddr, sockaddr_size),
|
|
SA_IN_SIZE(sockaddr_size), 0);
|
|
|
|
fip.found_ips = 0;
|
|
proc = htable_get(s->proc_table.db_dtls_ip, h, local_ip_cmp, &fip);
|
|
if (proc && fip.found_ips == 1)
|
|
return proc;
|
|
|
|
fip.found_ips = 0;
|
|
proc = htable_get(s->proc_table.db_ip, h, local_ip_cmp, &fip);
|
|
if (proc && fip.found_ips == 1)
|
|
return proc;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static bool dtls_id_cmp(const void* _c1, void* _c2)
|
|
{
|
|
const struct proc_st* c1 = _c1;
|
|
struct find_dtls_id_st* c2 = _c2;
|
|
|
|
if (c1->dtls_session_id_size != c2->dtls_id_size)
|
|
return 0;
|
|
|
|
if (memcmp(c1->dtls_session_id,
|
|
c2->dtls_id,
|
|
c1->dtls_session_id_size) == 0) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct proc_st *proc_search_dtls_id(struct main_server_st *s,
|
|
const uint8_t *id, unsigned id_size)
|
|
{
|
|
struct find_dtls_id_st fdtls_id;
|
|
|
|
fdtls_id.dtls_id = id;
|
|
fdtls_id.dtls_id_size = id_size;
|
|
|
|
return htable_get(s->proc_table.db_dtls_id, hash_any(id, id_size, 0), dtls_id_cmp, &fdtls_id);
|
|
}
|
|
|
|
static bool sid_cmp(const void* _c1, void* _c2)
|
|
{
|
|
const struct proc_st* c1 = _c1;
|
|
struct find_sid_st* c2 = _c2;
|
|
|
|
if (memcmp(c1->sid,
|
|
c2->sid,
|
|
sizeof(c1->sid)) == 0) {
|
|
return 1;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct proc_st *proc_search_sid(struct main_server_st *s,
|
|
const uint8_t sid[SID_SIZE])
|
|
{
|
|
struct find_sid_st fsid;
|
|
fsid.sid = sid;
|
|
|
|
return htable_get(s->proc_table.db_sid, hash_any(sid, SID_SIZE, 0), sid_cmp, &fsid);
|
|
}
|
|
|