nts: add NTS-NTP server and client
Add support for the NTS NTP extension fields.
This commit is contained in:
parent
a420ed57a1
commit
6043632f80
12 changed files with 1121 additions and 0 deletions
11
conf.c
11
conf.c
|
@ -230,6 +230,7 @@ static char *nts_server_key_file = NULL;
|
||||||
static int nts_server_port = 11443;
|
static int nts_server_port = 11443;
|
||||||
static int nts_server_processes = 1;
|
static int nts_server_processes = 1;
|
||||||
static int nts_server_connections = 100;
|
static int nts_server_connections = 100;
|
||||||
|
static int nts_refresh = 2419200; /* 4 weeks */
|
||||||
static int nts_rotate = 604800; /* 1 week */
|
static int nts_rotate = 604800; /* 1 week */
|
||||||
static char *nts_trusted_cert_file = NULL;
|
static char *nts_trusted_cert_file = NULL;
|
||||||
|
|
||||||
|
@ -556,6 +557,8 @@ CNF_ParseLine(const char *filename, int number, char *line)
|
||||||
parse_int(p, &nts_server_port);
|
parse_int(p, &nts_server_port);
|
||||||
} else if (!strcasecmp(command, "ntsprocesses")) {
|
} else if (!strcasecmp(command, "ntsprocesses")) {
|
||||||
parse_int(p, &nts_server_processes);
|
parse_int(p, &nts_server_processes);
|
||||||
|
} else if (!strcasecmp(command, "ntsrefresh")) {
|
||||||
|
parse_int(p, &nts_refresh);
|
||||||
} else if (!strcasecmp(command, "ntsrotate")) {
|
} else if (!strcasecmp(command, "ntsrotate")) {
|
||||||
parse_int(p, &nts_rotate);
|
parse_int(p, &nts_rotate);
|
||||||
} else if (!strcasecmp(command, "ntsservercert")) {
|
} else if (!strcasecmp(command, "ntsservercert")) {
|
||||||
|
@ -2113,6 +2116,14 @@ CNF_GetNtsServerConnections(void)
|
||||||
|
|
||||||
/* ================================================== */
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
CNF_GetNtsRefresh(void)
|
||||||
|
{
|
||||||
|
return nts_refresh;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
int
|
int
|
||||||
CNF_GetNtsRotate(void)
|
CNF_GetNtsRotate(void)
|
||||||
{
|
{
|
||||||
|
|
1
conf.h
1
conf.h
|
@ -145,6 +145,7 @@ extern char *CNF_GetNtsServerKeyFile(void);
|
||||||
extern int CNF_GetNtsServerPort(void);
|
extern int CNF_GetNtsServerPort(void);
|
||||||
extern int CNF_GetNtsServerProcesses(void);
|
extern int CNF_GetNtsServerProcesses(void);
|
||||||
extern int CNF_GetNtsServerConnections(void);
|
extern int CNF_GetNtsServerConnections(void);
|
||||||
|
extern int CNF_GetNtsRefresh(void);
|
||||||
extern int CNF_GetNtsRotate(void);
|
extern int CNF_GetNtsRotate(void);
|
||||||
extern char *CNF_GetNtsTrustedCertFile(void);
|
extern char *CNF_GetNtsTrustedCertFile(void);
|
||||||
extern int CNF_GetNoSystemCert(void);
|
extern int CNF_GetNoSystemCert(void);
|
||||||
|
|
1
configure
vendored
1
configure
vendored
|
@ -939,6 +939,7 @@ if [ $feat_ntp = "1" ] && [ $feat_nts = "1" ] && [ $try_gnutls = "1" ] && \
|
||||||
'gnutls_init(NULL, 0);'
|
'gnutls_init(NULL, 0);'
|
||||||
then
|
then
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ke_client.o nts_ke_server.o nts_ke_session.o"
|
||||||
|
EXTRA_OBJECTS="$EXTRA_OBJECTS nts_ntp_auth.o nts_ntp_client.o nts_ntp_server.o"
|
||||||
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
EXTRA_OBJECTS="$EXTRA_OBJECTS siv_nettle.o"
|
||||||
LIBS="$LIBS $test_link"
|
LIBS="$LIBS $test_link"
|
||||||
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
MYCPPFLAGS="$MYCPPFLAGS $test_cflags"
|
||||||
|
|
3
main.c
3
main.c
|
@ -40,6 +40,7 @@
|
||||||
#include "ntp_core.h"
|
#include "ntp_core.h"
|
||||||
#include "nts_ke_client.h"
|
#include "nts_ke_client.h"
|
||||||
#include "nts_ke_server.h"
|
#include "nts_ke_server.h"
|
||||||
|
#include "nts_ntp_server.h"
|
||||||
#include "socket.h"
|
#include "socket.h"
|
||||||
#include "sources.h"
|
#include "sources.h"
|
||||||
#include "sourcestats.h"
|
#include "sourcestats.h"
|
||||||
|
@ -117,6 +118,7 @@ MAI_CleanupAndExit(void)
|
||||||
CLG_Finalise();
|
CLG_Finalise();
|
||||||
NKC_Finalise();
|
NKC_Finalise();
|
||||||
NKS_Finalise();
|
NKS_Finalise();
|
||||||
|
NNS_Finalise();
|
||||||
NSD_Finalise();
|
NSD_Finalise();
|
||||||
NSR_Finalise();
|
NSR_Finalise();
|
||||||
SST_Finalise();
|
SST_Finalise();
|
||||||
|
@ -587,6 +589,7 @@ int main
|
||||||
SST_Initialise();
|
SST_Initialise();
|
||||||
NSR_Initialise();
|
NSR_Initialise();
|
||||||
NSD_Initialise();
|
NSD_Initialise();
|
||||||
|
NNS_Initialise();
|
||||||
NKS_Initialise(scfilter_level);
|
NKS_Initialise(scfilter_level);
|
||||||
NKC_Initialise();
|
NKC_Initialise();
|
||||||
CLG_Initialise();
|
CLG_Initialise();
|
||||||
|
|
41
nts_ntp.h
Normal file
41
nts_ntp.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2020
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Header file for the NTS-NTP protocol
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GOT_NTS_NTP_H
|
||||||
|
#define GOT_NTS_NTP_H
|
||||||
|
|
||||||
|
#define NTP_EF_NTS_UNIQUE_IDENTIFIER 0x0104
|
||||||
|
#define NTP_EF_NTS_COOKIE 0x0204
|
||||||
|
#define NTP_EF_NTS_COOKIE_PLACEHOLDER 0x0304
|
||||||
|
#define NTP_EF_NTS_AUTH_AND_EEF 0x0404
|
||||||
|
|
||||||
|
#define NTP_KOD_NTS_NAK 0x4e54534e
|
||||||
|
|
||||||
|
#define NTS_MIN_UNIQ_ID_LENGTH 32
|
||||||
|
#define NTS_MIN_UNPADDED_NONCE_LENGTH 16
|
||||||
|
#define NTS_MAX_COOKIES 8
|
||||||
|
|
||||||
|
#endif
|
174
nts_ntp_auth.c
Normal file
174
nts_ntp_auth.c
Normal file
|
@ -0,0 +1,174 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2020
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
NTS Authenticator and Encrypted Extension Fields extension field
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include "nts_ntp_auth.h"
|
||||||
|
|
||||||
|
#include "logging.h"
|
||||||
|
#include "ntp_ext.h"
|
||||||
|
#include "nts_ntp.h"
|
||||||
|
#include "siv.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct AuthHeader {
|
||||||
|
uint16_t nonce_length;
|
||||||
|
uint16_t ciphertext_length;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_padding_length(int length)
|
||||||
|
{
|
||||||
|
return length % 4U ? 4 - length % 4U : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_padded_length(int length)
|
||||||
|
{
|
||||||
|
return length + get_padding_length(length);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||||
|
const unsigned char *nonce, int nonce_length,
|
||||||
|
const unsigned char *plaintext, int plaintext_length,
|
||||||
|
int min_ef_length)
|
||||||
|
{
|
||||||
|
int auth_length, ciphertext_length, assoc_length;
|
||||||
|
int nonce_padding, ciphertext_padding, additional_padding;
|
||||||
|
unsigned char *ciphertext, *body;
|
||||||
|
struct AuthHeader *header;
|
||||||
|
|
||||||
|
assert(sizeof (*header) == 4);
|
||||||
|
|
||||||
|
if (nonce_length <= 0 || plaintext_length < 0) {
|
||||||
|
DEBUG_LOG("Invalid nonce/plaintext length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
assoc_length = info->length;
|
||||||
|
ciphertext_length = SIV_GetTagLength(siv) + plaintext_length;
|
||||||
|
nonce_padding = get_padding_length(nonce_length);
|
||||||
|
ciphertext_padding = get_padding_length(ciphertext_length);
|
||||||
|
min_ef_length = get_padded_length(min_ef_length);
|
||||||
|
|
||||||
|
auth_length = sizeof (*header) + nonce_length + nonce_padding +
|
||||||
|
ciphertext_length + ciphertext_padding;
|
||||||
|
additional_padding = MAX(min_ef_length - auth_length - 4, 0);
|
||||||
|
additional_padding = MAX(NTS_MIN_UNPADDED_NONCE_LENGTH - nonce_length - nonce_padding,
|
||||||
|
additional_padding);
|
||||||
|
auth_length += additional_padding;
|
||||||
|
|
||||||
|
if (!NEF_AddBlankField(packet, info, NTP_EF_NTS_AUTH_AND_EEF, auth_length,
|
||||||
|
(void **)&header)) {
|
||||||
|
DEBUG_LOG("Could not add EF");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
header->nonce_length = htons(nonce_length);
|
||||||
|
header->ciphertext_length = htons(ciphertext_length);
|
||||||
|
|
||||||
|
body = (unsigned char *)(header + 1);
|
||||||
|
ciphertext = body + nonce_length + nonce_padding;
|
||||||
|
|
||||||
|
memcpy(body, nonce, nonce_length);
|
||||||
|
memset(body + nonce_length, 0, nonce_padding);
|
||||||
|
|
||||||
|
if (!SIV_Encrypt(siv, nonce, nonce_length, packet, assoc_length,
|
||||||
|
plaintext, plaintext_length, ciphertext, ciphertext_length)) {
|
||||||
|
DEBUG_LOG("SIV encrypt failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
memset(ciphertext + ciphertext_length, 0, ciphertext_padding + additional_padding);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv, int ef_start,
|
||||||
|
unsigned char *plaintext, int buffer_length, int *plaintext_length)
|
||||||
|
{
|
||||||
|
unsigned int siv_tag_length, nonce_length, ciphertext_length;
|
||||||
|
unsigned char *nonce, *ciphertext;
|
||||||
|
int ef_type, ef_body_length;
|
||||||
|
void *ef_body;
|
||||||
|
struct AuthHeader *header;
|
||||||
|
|
||||||
|
if (!NEF_ParseField(packet, info->length, ef_start,
|
||||||
|
NULL, &ef_type, &ef_body, &ef_body_length))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (ef_type != NTP_EF_NTS_AUTH_AND_EEF)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
header = ef_body;
|
||||||
|
|
||||||
|
nonce_length = ntohs(header->nonce_length);
|
||||||
|
ciphertext_length = ntohs(header->ciphertext_length);
|
||||||
|
|
||||||
|
if (get_padded_length(nonce_length) +
|
||||||
|
get_padded_length(ciphertext_length) > ef_body_length)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
nonce = (unsigned char *)(header + 1);
|
||||||
|
ciphertext = (unsigned char *)(header + 1) + get_padded_length(nonce_length);
|
||||||
|
|
||||||
|
siv_tag_length = SIV_GetTagLength(siv);
|
||||||
|
|
||||||
|
if (nonce_length < 1 ||
|
||||||
|
ciphertext_length < siv_tag_length ||
|
||||||
|
ciphertext_length - siv_tag_length > buffer_length) {
|
||||||
|
DEBUG_LOG("Unexpected nonce/ciphertext length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ef_body_length < sizeof (*header) +
|
||||||
|
NTS_MIN_UNPADDED_NONCE_LENGTH + get_padded_length(ciphertext_length)) {
|
||||||
|
DEBUG_LOG("Missing padding");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
*plaintext_length = ciphertext_length - siv_tag_length;
|
||||||
|
|
||||||
|
if (!SIV_Decrypt(siv, nonce, nonce_length, packet, info->length - ef_body_length - 4,
|
||||||
|
ciphertext, ciphertext_length, plaintext, *plaintext_length)) {
|
||||||
|
DEBUG_LOG("SIV decrypt failed");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
43
nts_ntp_auth.h
Normal file
43
nts_ntp_auth.h
Normal file
|
@ -0,0 +1,43 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2020
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Header for NTS Authenticator and Encrypted Extension Fields
|
||||||
|
extension field
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GOT_NTS_NTP_AUTH_H
|
||||||
|
#define GOT_NTS_NTP_AUTH_H
|
||||||
|
|
||||||
|
#include "ntp.h"
|
||||||
|
#include "siv.h"
|
||||||
|
|
||||||
|
extern int NNA_GenerateAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||||
|
const unsigned char *nonce, int nonce_length,
|
||||||
|
const unsigned char *plaintext, int plaintext_length,
|
||||||
|
int min_ef_length);
|
||||||
|
|
||||||
|
extern int NNA_DecryptAuthEF(NTP_Packet *packet, NTP_PacketInfo *info, SIV_Instance siv,
|
||||||
|
int ef_start, unsigned char *plaintext, int buffer_length,
|
||||||
|
int *plaintext_length);
|
||||||
|
|
||||||
|
#endif
|
445
nts_ntp_client.c
Normal file
445
nts_ntp_client.c
Normal file
|
@ -0,0 +1,445 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2020
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Client NTS-NTP authentication
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include "nts_ntp_client.h"
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "ntp.h"
|
||||||
|
#include "ntp_ext.h"
|
||||||
|
#include "ntp_sources.h"
|
||||||
|
#include "nts_ke_client.h"
|
||||||
|
#include "nts_ntp.h"
|
||||||
|
#include "nts_ntp_auth.h"
|
||||||
|
#include "sched.h"
|
||||||
|
#include "siv.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
#define MAX_TOTAL_COOKIE_LENGTH (8 * 108)
|
||||||
|
#define MIN_NKE_RETRY_INTERVAL 1000
|
||||||
|
|
||||||
|
struct NNC_Instance_Record {
|
||||||
|
const IPSockAddr *ntp_address;
|
||||||
|
IPSockAddr nts_address;
|
||||||
|
char *name;
|
||||||
|
SIV_Instance siv_c2s;
|
||||||
|
SIV_Instance siv_s2c;
|
||||||
|
NKC_Instance nke;
|
||||||
|
|
||||||
|
struct timespec last_nke_attempt;
|
||||||
|
struct timespec last_nke_success;
|
||||||
|
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
||||||
|
int num_cookies;
|
||||||
|
int cookie_index;
|
||||||
|
int nak_response;
|
||||||
|
int ok_response;
|
||||||
|
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
|
||||||
|
unsigned char uniq_id[NTS_MIN_UNIQ_ID_LENGTH];
|
||||||
|
};
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static void
|
||||||
|
reset_instance(NNC_Instance inst)
|
||||||
|
{
|
||||||
|
UTI_ZeroTimespec(&inst->last_nke_attempt);
|
||||||
|
UTI_ZeroTimespec(&inst->last_nke_success);
|
||||||
|
inst->num_cookies = 0;
|
||||||
|
inst->cookie_index = 0;
|
||||||
|
inst->nak_response = 0;
|
||||||
|
inst->ok_response = 1;
|
||||||
|
memset(inst->nonce, 0, sizeof (inst->nonce));
|
||||||
|
memset(inst->uniq_id, 0, sizeof (inst->uniq_id));
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
NNC_Instance
|
||||||
|
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address)
|
||||||
|
{
|
||||||
|
NNC_Instance inst;
|
||||||
|
|
||||||
|
inst = MallocNew(struct NNC_Instance_Record);
|
||||||
|
|
||||||
|
inst->ntp_address = ntp_address;
|
||||||
|
inst->nts_address = *nts_address;
|
||||||
|
inst->name = name ? Strdup(name) : NULL;
|
||||||
|
inst->siv_c2s = NULL;
|
||||||
|
inst->siv_s2c = NULL;
|
||||||
|
inst->nke = NULL;
|
||||||
|
|
||||||
|
reset_instance(inst);
|
||||||
|
|
||||||
|
return inst;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
NNC_DestroyInstance(NNC_Instance inst)
|
||||||
|
{
|
||||||
|
if (inst->nke)
|
||||||
|
NKC_DestroyInstance(inst->nke);
|
||||||
|
if (inst->siv_c2s)
|
||||||
|
SIV_DestroyInstance(inst->siv_c2s);
|
||||||
|
if (inst->siv_s2c)
|
||||||
|
SIV_DestroyInstance(inst->siv_s2c);
|
||||||
|
|
||||||
|
Free(inst->name);
|
||||||
|
Free(inst);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
is_nke_needed(NNC_Instance inst)
|
||||||
|
{
|
||||||
|
struct timespec now;
|
||||||
|
|
||||||
|
/* Force NKE if a NAK was received since last valid auth */
|
||||||
|
if (inst->nak_response && !inst->ok_response && inst->num_cookies > 0) {
|
||||||
|
inst->num_cookies = 0;
|
||||||
|
DEBUG_LOG("Dropped cookies");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Force NKE if the keys encrypting the cookies are too old */
|
||||||
|
if (inst->num_cookies > 0) {
|
||||||
|
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||||
|
if (fabs(UTI_DiffTimespecsToDouble(&inst->last_nke_success, &now)) > CNF_GetNtsRefresh())
|
||||||
|
inst->num_cookies = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return inst->num_cookies == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
set_ntp_address(NNC_Instance inst, NTP_Remote_Address *negotiated_address)
|
||||||
|
{
|
||||||
|
NTP_Remote_Address old_address, new_address;
|
||||||
|
|
||||||
|
old_address = *inst->ntp_address;
|
||||||
|
new_address = *negotiated_address;
|
||||||
|
|
||||||
|
if (new_address.ip_addr.family == IPADDR_UNSPEC)
|
||||||
|
new_address.ip_addr = old_address.ip_addr;
|
||||||
|
if (new_address.port == 0)
|
||||||
|
new_address.port = old_address.port;
|
||||||
|
|
||||||
|
if (UTI_CompareIPs(&old_address.ip_addr, &new_address.ip_addr, NULL) == 0 &&
|
||||||
|
old_address.port == new_address.port)
|
||||||
|
/* Nothing to do */
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
if (NSR_UpdateSourceNtpAddress(&old_address, &new_address) != NSR_Success) {
|
||||||
|
LOG(LOGS_ERR, "Could not change %s to negotiated address %s",
|
||||||
|
UTI_IPToString(&old_address.ip_addr), UTI_IPToString(&new_address.ip_addr));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
get_nke_data(NNC_Instance inst)
|
||||||
|
{
|
||||||
|
NTP_Remote_Address ntp_address;
|
||||||
|
SIV_Algorithm siv;
|
||||||
|
NKE_Key c2s, s2c;
|
||||||
|
struct timespec now;
|
||||||
|
int got_data;
|
||||||
|
|
||||||
|
assert(is_nke_needed(inst));
|
||||||
|
|
||||||
|
if (!inst->nke) {
|
||||||
|
SCH_GetLastEventTime(&now, NULL, NULL);
|
||||||
|
if (fabs(UTI_DiffTimespecsToDouble(&inst->last_nke_attempt, &now)) <
|
||||||
|
MIN_NKE_RETRY_INTERVAL) {
|
||||||
|
DEBUG_LOG("Limiting NTS-KE request rate");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inst->name) {
|
||||||
|
LOG(LOGS_ERR, "Missing name of %s for NTS-KE",
|
||||||
|
UTI_IPToString(&inst->nts_address.ip_addr));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst->nke = NKC_CreateInstance(&inst->nts_address, inst->name);
|
||||||
|
|
||||||
|
if (!NKC_Start(inst->nke))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
inst->last_nke_attempt = now;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (NKC_IsActive(inst->nke))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
got_data = NKC_GetNtsData(inst->nke, &siv, &c2s, &s2c,
|
||||||
|
inst->cookies, &inst->num_cookies, NTS_MAX_COOKIES,
|
||||||
|
&ntp_address);
|
||||||
|
|
||||||
|
NKC_DestroyInstance(inst->nke);
|
||||||
|
inst->nke = NULL;
|
||||||
|
|
||||||
|
if (!got_data)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!set_ntp_address(inst, &ntp_address)) {
|
||||||
|
inst->num_cookies = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst->cookie_index = 0;
|
||||||
|
|
||||||
|
if (inst->siv_c2s)
|
||||||
|
SIV_DestroyInstance(inst->siv_c2s);
|
||||||
|
if (inst->siv_s2c)
|
||||||
|
SIV_DestroyInstance(inst->siv_s2c);
|
||||||
|
|
||||||
|
inst->siv_c2s = SIV_CreateInstance(siv);
|
||||||
|
inst->siv_s2c = SIV_CreateInstance(siv);
|
||||||
|
|
||||||
|
if (!inst->siv_c2s || !inst->siv_s2c ||
|
||||||
|
!SIV_SetKey(inst->siv_c2s, c2s.key, c2s.length) ||
|
||||||
|
!SIV_SetKey(inst->siv_s2c, s2c.key, s2c.length)) {
|
||||||
|
DEBUG_LOG("Could not initialise SIV");
|
||||||
|
inst->num_cookies = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
inst->nak_response = 0;
|
||||||
|
|
||||||
|
SCH_GetLastEventTime(&inst->last_nke_success, NULL, NULL);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NNC_PrepareForAuth(NNC_Instance inst)
|
||||||
|
{
|
||||||
|
if (is_nke_needed(inst)) {
|
||||||
|
if (!get_nke_data(inst))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTI_GetRandomBytes(&inst->uniq_id, sizeof (inst->uniq_id));
|
||||||
|
UTI_GetRandomBytes(&inst->nonce, sizeof (inst->nonce));
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
|
||||||
|
NTP_PacketInfo *info)
|
||||||
|
{
|
||||||
|
NKE_Cookie *cookie;
|
||||||
|
int i, req_cookies;
|
||||||
|
|
||||||
|
if (inst->num_cookies == 0 || !inst->siv_c2s)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (info->mode != MODE_CLIENT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
cookie = &inst->cookies[inst->cookie_index];
|
||||||
|
req_cookies = MIN(NTS_MAX_COOKIES - inst->num_cookies + 1,
|
||||||
|
MAX_TOTAL_COOKIE_LENGTH / (cookie->length + 4));
|
||||||
|
|
||||||
|
if (!NEF_AddField(packet, info, NTP_EF_NTS_UNIQUE_IDENTIFIER,
|
||||||
|
&inst->uniq_id, sizeof (inst->uniq_id)))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE,
|
||||||
|
cookie->cookie, cookie->length))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (i = 0; i < req_cookies - 1; i++) {
|
||||||
|
if (!NEF_AddField(packet, info, NTP_EF_NTS_COOKIE_PLACEHOLDER,
|
||||||
|
cookie->cookie, cookie->length))
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NNA_GenerateAuthEF(packet, info, inst->siv_c2s, inst->nonce, sizeof (inst->nonce),
|
||||||
|
(const unsigned char *)"", 0, NTP_MAX_V4_MAC_LENGTH + 4))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
inst->num_cookies--;
|
||||||
|
inst->cookie_index = (inst->cookie_index + 1) % NTS_MAX_COOKIES;
|
||||||
|
inst->ok_response = 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
static int
|
||||||
|
extract_cookies(NNC_Instance inst, unsigned char *plaintext, int length)
|
||||||
|
{
|
||||||
|
int ef_type, ef_body_length, ef_length, parsed, index, acceptable, saved;
|
||||||
|
void *ef_body;
|
||||||
|
|
||||||
|
acceptable = saved = 0;
|
||||||
|
|
||||||
|
for (parsed = 0; parsed < length; parsed += ef_length) {
|
||||||
|
if (!NEF_ParseSingleField(plaintext, length, parsed,
|
||||||
|
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (ef_type != NTP_EF_NTS_COOKIE)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
if (ef_length < NTP_MIN_EF_LENGTH || ef_body_length > sizeof (inst->cookies[0].cookie)) {
|
||||||
|
DEBUG_LOG("Unexpected cookie length %d", ef_body_length);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
acceptable++;
|
||||||
|
|
||||||
|
if (inst->num_cookies >= NTS_MAX_COOKIES)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
index = (inst->cookie_index + inst->num_cookies) % NTS_MAX_COOKIES;
|
||||||
|
memcpy(inst->cookies[index].cookie, ef_body, ef_body_length);
|
||||||
|
inst->cookies[index].length = ef_body_length;
|
||||||
|
inst->num_cookies++;
|
||||||
|
|
||||||
|
saved++;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("Extracted %d cookies (saved %d)", acceptable, saved);
|
||||||
|
|
||||||
|
return acceptable > 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
|
||||||
|
NTP_PacketInfo *info)
|
||||||
|
{
|
||||||
|
int ef_type, ef_body_length, ef_length, parsed, plaintext_length;
|
||||||
|
int has_valid_uniq_id = 0, has_valid_auth = 0;
|
||||||
|
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
|
||||||
|
void *ef_body;
|
||||||
|
|
||||||
|
if (info->ext_fields == 0 || info->mode != MODE_SERVER)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Accept only one response per request */
|
||||||
|
if (inst->ok_response)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
if (!inst->siv_s2c)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
|
||||||
|
if (!NEF_ParseField(packet, info->length, parsed,
|
||||||
|
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (ef_type) {
|
||||||
|
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
|
||||||
|
if (ef_body_length != sizeof (inst->uniq_id) ||
|
||||||
|
memcmp(ef_body, inst->uniq_id, sizeof (inst->uniq_id)) != 0) {
|
||||||
|
DEBUG_LOG("Invalid uniq id");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
has_valid_uniq_id = 1;
|
||||||
|
break;
|
||||||
|
case NTP_EF_NTS_COOKIE:
|
||||||
|
DEBUG_LOG("Unencrypted cookie");
|
||||||
|
break;
|
||||||
|
case NTP_EF_NTS_AUTH_AND_EEF:
|
||||||
|
if (parsed + ef_length != info->length) {
|
||||||
|
DEBUG_LOG("Auth not last EF");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NNA_DecryptAuthEF(packet, info, inst->siv_s2c, parsed,
|
||||||
|
plaintext, sizeof (plaintext), &plaintext_length))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
has_valid_auth = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_valid_uniq_id || !has_valid_auth) {
|
||||||
|
if (has_valid_uniq_id && packet->stratum == NTP_INVALID_STRATUM &&
|
||||||
|
ntohl(packet->reference_id) == NTP_KOD_NTS_NAK) {
|
||||||
|
DEBUG_LOG("NTS NAK");
|
||||||
|
inst->nak_response = 1;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DEBUG_LOG("Missing NTS EF");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!extract_cookies(inst, plaintext, plaintext_length))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
inst->ok_response = 1;
|
||||||
|
|
||||||
|
/* At this point we know the client interoperates with the server. Allow a
|
||||||
|
new NTS-KE session to be started as soon as the cookies run out. */
|
||||||
|
UTI_ZeroTimespec(&inst->last_nke_attempt);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
|
||||||
|
{
|
||||||
|
if (inst->nke)
|
||||||
|
NKC_DestroyInstance(inst->nke);
|
||||||
|
|
||||||
|
inst->nke = NULL;
|
||||||
|
inst->num_cookies = 0;
|
||||||
|
inst->nts_address.ip_addr = *address;
|
||||||
|
|
||||||
|
reset_instance(inst);
|
||||||
|
|
||||||
|
DEBUG_LOG("NTS reset");
|
||||||
|
}
|
46
nts_ntp_client.h
Normal file
46
nts_ntp_client.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2020
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Header file for client NTS-NTP authentication
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GOT_NTS_NTP_CLIENT_H
|
||||||
|
#define GOT_NTS_NTP_CLIENT_H
|
||||||
|
|
||||||
|
#include "addressing.h"
|
||||||
|
#include "ntp.h"
|
||||||
|
|
||||||
|
typedef struct NNC_Instance_Record *NNC_Instance;
|
||||||
|
|
||||||
|
extern NNC_Instance NNC_CreateInstance(IPSockAddr *nts_address, const char *name,
|
||||||
|
const IPSockAddr *ntp_address);
|
||||||
|
extern void NNC_DestroyInstance(NNC_Instance inst);
|
||||||
|
extern int NNC_PrepareForAuth(NNC_Instance inst);
|
||||||
|
extern int NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet,
|
||||||
|
NTP_PacketInfo *info);
|
||||||
|
extern int NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet,
|
||||||
|
NTP_PacketInfo *info);
|
||||||
|
|
||||||
|
extern void NNC_ChangeAddress(NNC_Instance inst, IPAddr *address);
|
||||||
|
|
||||||
|
#endif
|
253
nts_ntp_server.c
Normal file
253
nts_ntp_server.c
Normal file
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2020
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Server NTS-NTP authentication
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "config.h"
|
||||||
|
|
||||||
|
#include "sysincl.h"
|
||||||
|
|
||||||
|
#include "nts_ntp_server.h"
|
||||||
|
|
||||||
|
#include "conf.h"
|
||||||
|
#include "logging.h"
|
||||||
|
#include "memory.h"
|
||||||
|
#include "ntp.h"
|
||||||
|
#include "ntp_ext.h"
|
||||||
|
#include "nts_ke_server.h"
|
||||||
|
#include "nts_ntp.h"
|
||||||
|
#include "nts_ntp_auth.h"
|
||||||
|
#include "siv.h"
|
||||||
|
#include "util.h"
|
||||||
|
|
||||||
|
struct NtsServer {
|
||||||
|
SIV_Instance siv;
|
||||||
|
unsigned char nonce[NTS_MIN_UNPADDED_NONCE_LENGTH];
|
||||||
|
NKE_Cookie cookies[NTS_MAX_COOKIES];
|
||||||
|
int num_cookies;
|
||||||
|
NTP_int64 req_tx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The server instance handling all requests */
|
||||||
|
struct NtsServer *server;
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
NNS_Initialise(void)
|
||||||
|
{
|
||||||
|
/* Create an NTS-NTP server instance only if NTS-KE server is enabled */
|
||||||
|
if (!CNF_GetNtsServerCertFile() || !CNF_GetNtsServerKeyFile()) {
|
||||||
|
server = NULL;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
server = Malloc(sizeof (struct NtsServer));
|
||||||
|
server->siv = SIV_CreateInstance(AEAD_AES_SIV_CMAC_256);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
void
|
||||||
|
NNS_Finalise(void)
|
||||||
|
{
|
||||||
|
if (!server)
|
||||||
|
return;
|
||||||
|
|
||||||
|
SIV_DestroyInstance(server->siv);
|
||||||
|
Free(server);
|
||||||
|
server = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
||||||
|
{
|
||||||
|
int ef_type, ef_body_length, ef_length, has_uniq_id = 0, has_auth = 0, has_cookie = 0;
|
||||||
|
int i, plaintext_length, parsed, requested_cookies, cookie_length = -1, auth_start = 0;
|
||||||
|
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
|
||||||
|
NKE_Cookie cookie;
|
||||||
|
NKE_Key c2s, s2c;
|
||||||
|
void *ef_body;
|
||||||
|
|
||||||
|
if (!server)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*kod = 0;
|
||||||
|
|
||||||
|
server->num_cookies = 0;
|
||||||
|
server->req_tx = packet->transmit_ts;
|
||||||
|
|
||||||
|
if (info->ext_fields == 0 || info->mode != MODE_CLIENT)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
requested_cookies = 0;
|
||||||
|
|
||||||
|
for (parsed = NTP_HEADER_LENGTH; parsed < info->length; parsed += ef_length) {
|
||||||
|
if (!NEF_ParseField(packet, info->length, parsed,
|
||||||
|
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (ef_type) {
|
||||||
|
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
|
||||||
|
has_uniq_id = 1;
|
||||||
|
break;
|
||||||
|
case NTP_EF_NTS_COOKIE:
|
||||||
|
if (has_cookie || ef_body_length > sizeof (cookie.cookie))
|
||||||
|
return 0;
|
||||||
|
cookie.length = ef_body_length;
|
||||||
|
memcpy(cookie.cookie, ef_body, ef_body_length);
|
||||||
|
has_cookie = 1;
|
||||||
|
/* Fall through */
|
||||||
|
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
|
||||||
|
requested_cookies++;
|
||||||
|
|
||||||
|
if (cookie_length >= 0 && cookie_length != ef_body_length) {
|
||||||
|
DEBUG_LOG("Invalid cookie/placeholder length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
cookie_length = ef_body_length;
|
||||||
|
break;
|
||||||
|
case NTP_EF_NTS_AUTH_AND_EEF:
|
||||||
|
auth_start = parsed;
|
||||||
|
has_auth = 1;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!has_uniq_id || !has_cookie || !has_auth) {
|
||||||
|
DEBUG_LOG("Missing an NTS EF");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NKS_DecodeCookie(&cookie, &c2s, &s2c)) {
|
||||||
|
*kod = NTP_KOD_NTS_NAK;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SIV_SetKey(server->siv, c2s.key, c2s.length)) {
|
||||||
|
DEBUG_LOG("Could not set C2S key");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!NNA_DecryptAuthEF(packet, info, server->siv, auth_start,
|
||||||
|
plaintext, sizeof (plaintext), &plaintext_length)) {
|
||||||
|
*kod = NTP_KOD_NTS_NAK;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
for (parsed = 0; parsed < plaintext_length; parsed += ef_length) {
|
||||||
|
if (!NEF_ParseSingleField(plaintext, plaintext_length, parsed,
|
||||||
|
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (ef_type) {
|
||||||
|
case NTP_EF_NTS_COOKIE_PLACEHOLDER:
|
||||||
|
if (cookie_length != ef_body_length) {
|
||||||
|
DEBUG_LOG("Invalid cookie/placeholder length");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
requested_cookies++;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!SIV_SetKey(server->siv, s2c.key, s2c.length)) {
|
||||||
|
DEBUG_LOG("Could not set S2C key");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
UTI_GetRandomBytes(server->nonce, sizeof (server->nonce));
|
||||||
|
|
||||||
|
server->num_cookies = MIN(NTS_MAX_COOKIES, requested_cookies);
|
||||||
|
for (i = 0; i < server->num_cookies; i++)
|
||||||
|
if (!NKS_GenerateCookie(&c2s, &s2c, &server->cookies[i]))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ================================================== */
|
||||||
|
|
||||||
|
int
|
||||||
|
NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
||||||
|
NTP_Packet *response, NTP_PacketInfo *res_info,
|
||||||
|
uint32_t kod)
|
||||||
|
{
|
||||||
|
int i, ef_type, ef_body_length, ef_length, parsed;
|
||||||
|
void *ef_body;
|
||||||
|
unsigned char plaintext[NTP_MAX_EXTENSIONS_LENGTH];
|
||||||
|
int plaintext_length;
|
||||||
|
|
||||||
|
if (!server || req_info->mode != MODE_CLIENT || res_info->mode != MODE_SERVER)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
/* Make sure this is a response to the expected request */
|
||||||
|
if (UTI_CompareNtp64(&server->req_tx, &request->transmit_ts) != 0)
|
||||||
|
assert(0);
|
||||||
|
|
||||||
|
for (parsed = NTP_HEADER_LENGTH; parsed < req_info->length; parsed += ef_length) {
|
||||||
|
if (!NEF_ParseField(request, req_info->length, parsed,
|
||||||
|
&ef_length, &ef_type, &ef_body, &ef_body_length))
|
||||||
|
break;
|
||||||
|
|
||||||
|
switch (ef_type) {
|
||||||
|
case NTP_EF_NTS_UNIQUE_IDENTIFIER:
|
||||||
|
/* Copy the ID from the request */
|
||||||
|
if (!NEF_AddField(response, res_info, ef_type, ef_body, ef_body_length))
|
||||||
|
return 0;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* NTS NAK response does not have any other fields */
|
||||||
|
if (kod)
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
for (i = 0, plaintext_length = 0; i < server->num_cookies; i++) {
|
||||||
|
if (!NEF_SetField(plaintext, sizeof (plaintext), plaintext_length,
|
||||||
|
NTP_EF_NTS_COOKIE, &server->cookies[i].cookie,
|
||||||
|
server->cookies[i].length, &ef_length))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
plaintext_length += ef_length;
|
||||||
|
assert(plaintext_length <= sizeof (plaintext));
|
||||||
|
}
|
||||||
|
|
||||||
|
server->num_cookies = 0;
|
||||||
|
|
||||||
|
if (!NNA_GenerateAuthEF(response, res_info, server->siv,
|
||||||
|
server->nonce, sizeof (server->nonce),
|
||||||
|
plaintext, plaintext_length,
|
||||||
|
req_info->length - res_info->length))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
40
nts_ntp_server.h
Normal file
40
nts_ntp_server.h
Normal file
|
@ -0,0 +1,40 @@
|
||||||
|
/*
|
||||||
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
||||||
|
|
||||||
|
**********************************************************************
|
||||||
|
* Copyright (C) Miroslav Lichvar 2020
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify
|
||||||
|
* it under the terms of version 2 of the GNU General Public License as
|
||||||
|
* published by the Free Software Foundation.
|
||||||
|
*
|
||||||
|
* 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, write to the Free Software Foundation, Inc.,
|
||||||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||||
|
*
|
||||||
|
**********************************************************************
|
||||||
|
|
||||||
|
=======================================================================
|
||||||
|
|
||||||
|
Header file for server NTS-NTP authentication
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef GOT_NTS_NTP_SERVER_H
|
||||||
|
#define GOT_NTS_NTP_SERVER_H
|
||||||
|
|
||||||
|
#include "ntp.h"
|
||||||
|
|
||||||
|
extern void NNS_Initialise(void);
|
||||||
|
extern void NNS_Finalise(void);
|
||||||
|
|
||||||
|
extern int NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod);
|
||||||
|
extern int NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
||||||
|
NTP_Packet *response, NTP_PacketInfo *res_info,
|
||||||
|
uint32_t kod);
|
||||||
|
|
||||||
|
#endif
|
63
stubs.c
63
stubs.c
|
@ -42,6 +42,8 @@
|
||||||
#include "ntp_signd.h"
|
#include "ntp_signd.h"
|
||||||
#include "nts_ke_client.h"
|
#include "nts_ke_client.h"
|
||||||
#include "nts_ke_server.h"
|
#include "nts_ke_server.h"
|
||||||
|
#include "nts_ntp_client.h"
|
||||||
|
#include "nts_ntp_server.h"
|
||||||
#include "privops.h"
|
#include "privops.h"
|
||||||
#include "refclock.h"
|
#include "refclock.h"
|
||||||
#include "sched.h"
|
#include "sched.h"
|
||||||
|
@ -452,6 +454,67 @@ CMC_DestroyInstance(CMC_Instance inst)
|
||||||
|
|
||||||
#ifndef FEAT_NTS
|
#ifndef FEAT_NTS
|
||||||
|
|
||||||
|
void
|
||||||
|
NNS_Initialise(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NNS_Finalise(void)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
NNS_CheckRequestAuth(NTP_Packet *packet, NTP_PacketInfo *info, uint32_t *kod)
|
||||||
|
{
|
||||||
|
*kod = 0;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
NNS_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *req_info,
|
||||||
|
NTP_Packet *response, NTP_PacketInfo *res_info,
|
||||||
|
uint32_t kod)
|
||||||
|
{
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
NNC_Instance
|
||||||
|
NNC_CreateInstance(IPSockAddr *nts_address, const char *name, const IPSockAddr *ntp_address)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NNC_DestroyInstance(NNC_Instance inst)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
NNC_PrepareForAuth(NNC_Instance inst)
|
||||||
|
{
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
NNC_GenerateRequestAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
|
||||||
|
{
|
||||||
|
DEBUG_LOG("NTS support disabled");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
int
|
||||||
|
NNC_CheckResponseAuth(NNC_Instance inst, NTP_Packet *packet, NTP_PacketInfo *info)
|
||||||
|
{
|
||||||
|
DEBUG_LOG("NTS support disabled");
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void
|
||||||
|
NNC_ChangeAddress(NNC_Instance inst, IPAddr *address)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
void
|
void
|
||||||
NKC_Initialise(void)
|
NKC_Initialise(void)
|
||||||
{
|
{
|
||||||
|
|
Loading…
Reference in a new issue