It was never used for anything and messages in debug output already include filenames, which can be easily grepped if there is a need to see log messages only from a particular file.
379 lines
9.6 KiB
C
379 lines
9.6 KiB
C
/*
|
|
chronyd/chronyc - Programs for keeping computer clocks accurate.
|
|
|
|
**********************************************************************
|
|
* Copyright (C) Miroslav Lichvar 2016
|
|
*
|
|
* 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.
|
|
*
|
|
**********************************************************************
|
|
|
|
=======================================================================
|
|
|
|
Support for MS-SNTP authentication in Samba (ntp_signd)
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include "sysincl.h"
|
|
|
|
#include "array.h"
|
|
#include "conf.h"
|
|
#include "logging.h"
|
|
#include "ntp_io.h"
|
|
#include "ntp_signd.h"
|
|
#include "sched.h"
|
|
#include "util.h"
|
|
|
|
/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
|
|
|
|
#define SIGND_VERSION 0
|
|
|
|
typedef enum {
|
|
SIGN_TO_CLIENT = 0,
|
|
ASK_SERVER_TO_SIGN = 1,
|
|
CHECK_SERVER_SIGNATURE = 2,
|
|
SIGNING_SUCCESS = 3,
|
|
SIGNING_FAILURE = 4,
|
|
} SigndOp;
|
|
|
|
typedef struct {
|
|
uint32_t length;
|
|
uint32_t version;
|
|
uint32_t op;
|
|
uint16_t packet_id;
|
|
uint16_t _pad;
|
|
uint32_t key_id;
|
|
NTP_Packet packet_to_sign;
|
|
} SigndRequest;
|
|
|
|
typedef struct {
|
|
uint32_t length;
|
|
uint32_t version;
|
|
uint32_t op;
|
|
uint32_t packet_id;
|
|
NTP_Packet signed_packet;
|
|
} SigndResponse;
|
|
|
|
typedef struct {
|
|
NTP_Remote_Address remote_addr;
|
|
NTP_Local_Address local_addr;
|
|
|
|
int sent;
|
|
int received;
|
|
int request_length;
|
|
struct timespec request_ts;
|
|
SigndRequest request;
|
|
SigndResponse response;
|
|
} SignInstance;
|
|
|
|
/* As the communication with ntp_signd is asynchronous, incoming packets are
|
|
saved in a queue in order to avoid loss when they come in bursts */
|
|
|
|
#define MAX_QUEUE_LENGTH 16U
|
|
#define NEXT_QUEUE_INDEX(index) (((index) + 1) % MAX_QUEUE_LENGTH)
|
|
#define IS_QUEUE_EMPTY() (queue_head == queue_tail)
|
|
|
|
/* Fixed-size array of SignInstance */
|
|
static ARR_Instance queue;
|
|
static unsigned int queue_head;
|
|
static unsigned int queue_tail;
|
|
|
|
#define INVALID_SOCK_FD -1
|
|
|
|
/* Unix domain socket connected to ntp_signd */
|
|
static int sock_fd;
|
|
|
|
#define MIN_AUTH_DELAY 1.0e-5
|
|
#define MAX_AUTH_DELAY 1.0e-2
|
|
|
|
/* Average time needed for signing one packet. This is used to adjust the
|
|
transmit timestamp in NTP packets. The timestamp won't be very accurate as
|
|
the delay is variable, but it should be good enough for MS-SNTP clients. */
|
|
static double auth_delay;
|
|
|
|
/* Flag indicating if the MS-SNTP authentication is enabled */
|
|
static int enabled;
|
|
|
|
/* ================================================== */
|
|
|
|
static void read_write_socket(int sock_fd, int event, void *anything);
|
|
|
|
/* ================================================== */
|
|
|
|
static void
|
|
close_socket(void)
|
|
{
|
|
SCH_RemoveFileHandler(sock_fd);
|
|
close(sock_fd);
|
|
sock_fd = INVALID_SOCK_FD;
|
|
|
|
/* Empty the queue */
|
|
queue_head = queue_tail = 0;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static int
|
|
open_socket(void)
|
|
{
|
|
struct sockaddr_un s;
|
|
|
|
if (sock_fd >= 0)
|
|
return 1;
|
|
|
|
sock_fd = socket(AF_UNIX, SOCK_STREAM, 0);
|
|
if (sock_fd < 0) {
|
|
DEBUG_LOG("Could not open signd socket : %s", strerror(errno));
|
|
return 0;
|
|
}
|
|
|
|
UTI_FdSetCloexec(sock_fd);
|
|
SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
|
|
|
|
s.sun_family = AF_UNIX;
|
|
if (snprintf(s.sun_path, sizeof (s.sun_path), "%s/socket",
|
|
CNF_GetNtpSigndSocket()) >= sizeof (s.sun_path)) {
|
|
DEBUG_LOG("signd socket path too long");
|
|
close_socket();
|
|
return 0;
|
|
}
|
|
|
|
if (connect(sock_fd, (struct sockaddr *)&s, sizeof (s)) < 0) {
|
|
DEBUG_LOG("Could not connect to signd : %s", strerror(errno));
|
|
close_socket();
|
|
return 0;
|
|
}
|
|
|
|
DEBUG_LOG("Connected to signd");
|
|
|
|
return 1;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static void
|
|
process_response(SignInstance *inst)
|
|
{
|
|
struct timespec ts;
|
|
double delay;
|
|
|
|
if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
|
|
DEBUG_LOG("Invalid response ID");
|
|
return;
|
|
}
|
|
|
|
if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
|
|
DEBUG_LOG("Signing failed");
|
|
return;
|
|
}
|
|
|
|
/* Check if the file descriptor is still valid */
|
|
if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
|
|
DEBUG_LOG("Invalid NTP socket");
|
|
return;
|
|
}
|
|
|
|
SCH_GetLastEventTime(NULL, NULL, &ts);
|
|
delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
|
|
|
|
DEBUG_LOG("Signing succeeded (delay %f)", delay);
|
|
|
|
/* Send the signed NTP packet */
|
|
NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
|
|
ntohl(inst->response.length) + sizeof (inst->response.length) -
|
|
offsetof(SigndResponse, signed_packet), 0);
|
|
|
|
/* Update exponential moving average of the authentication delay */
|
|
delay = CLAMP(MIN_AUTH_DELAY, delay, MAX_AUTH_DELAY);
|
|
auth_delay += 0.1 * (delay - auth_delay);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
static void
|
|
read_write_socket(int sock_fd, int event, void *anything)
|
|
{
|
|
SignInstance *inst;
|
|
uint32_t response_length;
|
|
int s;
|
|
|
|
inst = ARR_GetElement(queue, queue_head);
|
|
|
|
if (event == SCH_FILE_OUTPUT) {
|
|
assert(!IS_QUEUE_EMPTY());
|
|
assert(inst->sent < inst->request_length);
|
|
|
|
if (!inst->sent)
|
|
SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
|
|
|
|
s = send(sock_fd, (char *)&inst->request + inst->sent,
|
|
inst->request_length - inst->sent, 0);
|
|
|
|
if (s < 0) {
|
|
DEBUG_LOG("signd socket error: %s", strerror(errno));
|
|
close_socket();
|
|
return;
|
|
}
|
|
|
|
DEBUG_LOG("Sent %d bytes to signd", s);
|
|
inst->sent += s;
|
|
|
|
/* Try again later if the request is not complete yet */
|
|
if (inst->sent < inst->request_length)
|
|
return;
|
|
|
|
/* Disable output and wait for a response */
|
|
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT);
|
|
}
|
|
|
|
if (event == SCH_FILE_INPUT) {
|
|
if (IS_QUEUE_EMPTY()) {
|
|
DEBUG_LOG("Unexpected signd response");
|
|
close_socket();
|
|
return;
|
|
}
|
|
|
|
assert(inst->received < sizeof (inst->response));
|
|
s = recv(sock_fd, (char *)&inst->response + inst->received,
|
|
sizeof (inst->response) - inst->received, 0);
|
|
|
|
if (s <= 0) {
|
|
if (s < 0)
|
|
DEBUG_LOG("signd socket error: %s", strerror(errno));
|
|
else
|
|
DEBUG_LOG("signd socket closed");
|
|
|
|
close_socket();
|
|
return;
|
|
}
|
|
|
|
DEBUG_LOG("Received %d bytes from signd", s);
|
|
inst->received += s;
|
|
|
|
if (inst->received < sizeof (inst->response.length))
|
|
return;
|
|
|
|
response_length = ntohl(inst->response.length) + sizeof (inst->response.length);
|
|
|
|
if (response_length < offsetof(SigndResponse, signed_packet) ||
|
|
response_length > sizeof (SigndResponse)) {
|
|
DEBUG_LOG("Invalid response length");
|
|
close_socket();
|
|
return;
|
|
}
|
|
|
|
/* Wait for more data if not complete yet */
|
|
if (inst->received < response_length)
|
|
return;
|
|
|
|
process_response(inst);
|
|
|
|
/* Move the head and enable output for the next packet */
|
|
queue_head = NEXT_QUEUE_INDEX(queue_head);
|
|
if (!IS_QUEUE_EMPTY())
|
|
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
|
|
}
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
NSD_Initialise()
|
|
{
|
|
sock_fd = INVALID_SOCK_FD;
|
|
auth_delay = MIN_AUTH_DELAY;
|
|
enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
|
|
|
|
if (!enabled)
|
|
return;
|
|
|
|
queue = ARR_CreateInstance(sizeof (SignInstance));
|
|
ARR_SetSize(queue, MAX_QUEUE_LENGTH);
|
|
queue_head = queue_tail = 0;
|
|
|
|
LOG(LOGS_INFO, "MS-SNTP authentication enabled");
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
void
|
|
NSD_Finalise()
|
|
{
|
|
if (!enabled)
|
|
return;
|
|
if (sock_fd != INVALID_SOCK_FD)
|
|
close_socket();
|
|
ARR_DestroyInstance(queue);
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
extern int NSD_GetAuthDelay(uint32_t key_id)
|
|
{
|
|
return 1.0e9 * auth_delay;
|
|
}
|
|
|
|
/* ================================================== */
|
|
|
|
int
|
|
NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, int length)
|
|
{
|
|
SignInstance *inst;
|
|
|
|
if (!enabled) {
|
|
DEBUG_LOG("signd disabled");
|
|
return 0;
|
|
}
|
|
|
|
if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
|
|
DEBUG_LOG("signd queue full");
|
|
return 0;
|
|
}
|
|
|
|
if (length != NTP_NORMAL_PACKET_LENGTH) {
|
|
DEBUG_LOG("Invalid packet length");
|
|
return 0;
|
|
}
|
|
|
|
if (!open_socket())
|
|
return 0;
|
|
|
|
inst = ARR_GetElement(queue, queue_tail);
|
|
inst->remote_addr = *remote_addr;
|
|
inst->local_addr = *local_addr;
|
|
inst->sent = 0;
|
|
inst->received = 0;
|
|
inst->request_length = offsetof(SigndRequest, packet_to_sign) + length;
|
|
|
|
/* The length field doesn't include itself */
|
|
inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
|
|
inst->request.version = htonl(SIGND_VERSION);
|
|
inst->request.op = htonl(SIGN_TO_CLIENT);
|
|
inst->request.packet_id = htons(queue_tail);
|
|
inst->request._pad = 0;
|
|
inst->request.key_id = htonl(key_id);
|
|
|
|
memcpy(&inst->request.packet_to_sign, packet, length);
|
|
|
|
/* Enable output if there was no pending request */
|
|
if (IS_QUEUE_EMPTY())
|
|
SCH_SetFileHandlerEvents(sock_fd, SCH_FILE_INPUT | SCH_FILE_OUTPUT);
|
|
|
|
queue_tail = NEXT_QUEUE_INDEX(queue_tail);
|
|
|
|
DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
|
|
|
|
return 1;
|
|
}
|