]> git.ipfire.org Git - thirdparty/chrony.git/blame - ntp_signd.c
conf: rework allow/deny parser
[thirdparty/chrony.git] / ntp_signd.c
CommitLineData
577aed48
ML
1/*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Miroslav Lichvar 2016
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of version 2 of the GNU General Public License as
9 * published by the Free Software Foundation.
10 *
11 * This program is distributed in the hope that it will be useful, but
12 * WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License along
17 * with this program; if not, write to the Free Software Foundation, Inc.,
18 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
19 *
20 **********************************************************************
21
22 =======================================================================
23
24 Support for MS-SNTP authentication in Samba (ntp_signd)
25 */
26
27#include "config.h"
28
29#include "sysincl.h"
30
31#include "array.h"
32#include "conf.h"
33#include "logging.h"
34#include "ntp_io.h"
35#include "ntp_signd.h"
36#include "sched.h"
2de24cfd 37#include "socket.h"
577aed48
ML
38#include "util.h"
39
40/* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
41
42#define SIGND_VERSION 0
43
44typedef enum {
45 SIGN_TO_CLIENT = 0,
46 ASK_SERVER_TO_SIGN = 1,
47 CHECK_SERVER_SIGNATURE = 2,
48 SIGNING_SUCCESS = 3,
49 SIGNING_FAILURE = 4,
50} SigndOp;
51
52typedef struct {
53 uint32_t length;
54 uint32_t version;
55 uint32_t op;
56 uint16_t packet_id;
57 uint16_t _pad;
58 uint32_t key_id;
59 NTP_Packet packet_to_sign;
60} SigndRequest;
61
62typedef struct {
63 uint32_t length;
64 uint32_t version;
65 uint32_t op;
66 uint32_t packet_id;
67 NTP_Packet signed_packet;
68} SigndResponse;
69
70typedef struct {
71 NTP_Remote_Address remote_addr;
72 NTP_Local_Address local_addr;
73
74 int sent;
75 int received;
76 int request_length;
d0dfa1de 77 struct timespec request_ts;
577aed48
ML
78 SigndRequest request;
79 SigndResponse response;
80} SignInstance;
81
82/* As the communication with ntp_signd is asynchronous, incoming packets are
83 saved in a queue in order to avoid loss when they come in bursts */
84
85#define MAX_QUEUE_LENGTH 16U
86#define NEXT_QUEUE_INDEX(index) (((index) + 1) % MAX_QUEUE_LENGTH)
87#define IS_QUEUE_EMPTY() (queue_head == queue_tail)
88
89/* Fixed-size array of SignInstance */
90static ARR_Instance queue;
91static unsigned int queue_head;
92static unsigned int queue_tail;
93
2de24cfd 94#define INVALID_SOCK_FD (-6)
577aed48
ML
95
96/* Unix domain socket connected to ntp_signd */
97static int sock_fd;
98
577aed48
ML
99/* Flag indicating if the MS-SNTP authentication is enabled */
100static int enabled;
101
102/* ================================================== */
103
104static void read_write_socket(int sock_fd, int event, void *anything);
105
106/* ================================================== */
107
108static void
109close_socket(void)
110{
111 SCH_RemoveFileHandler(sock_fd);
2de24cfd 112 SCK_CloseSocket(sock_fd);
577aed48
ML
113 sock_fd = INVALID_SOCK_FD;
114
115 /* Empty the queue */
116 queue_head = queue_tail = 0;
117}
118
119/* ================================================== */
120
121static int
122open_socket(void)
123{
2fc8edac 124 char path[PATH_MAX];
577aed48 125
2de24cfd 126 if (sock_fd != INVALID_SOCK_FD)
577aed48
ML
127 return 1;
128
2de24cfd 129 if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) {
f282856c 130 DEBUG_LOG("signd socket path too long");
577aed48
ML
131 return 0;
132 }
133
2de24cfd
ML
134 sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
135 if (sock_fd < 0) {
136 sock_fd = INVALID_SOCK_FD;
577aed48
ML
137 return 0;
138 }
139
2de24cfd 140 SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
577aed48
ML
141
142 return 1;
143}
144
145/* ================================================== */
146
147static void
148process_response(SignInstance *inst)
149{
d0dfa1de 150 struct timespec ts;
577aed48
ML
151 double delay;
152
153 if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
f282856c 154 DEBUG_LOG("Invalid response ID");
577aed48
ML
155 return;
156 }
157
158 if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
f282856c 159 DEBUG_LOG("Signing failed");
577aed48
ML
160 return;
161 }
162
163 /* Check if the file descriptor is still valid */
164 if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
f282856c 165 DEBUG_LOG("Invalid NTP socket");
577aed48
ML
166 return;
167 }
168
d0dfa1de 169 SCH_GetLastEventTime(NULL, NULL, &ts);
cfe706f0 170 delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
577aed48 171
f282856c 172 DEBUG_LOG("Signing succeeded (delay %f)", delay);
577aed48
ML
173
174 /* Send the signed NTP packet */
175 NIO_SendPacket(&inst->response.signed_packet, &inst->remote_addr, &inst->local_addr,
176 ntohl(inst->response.length) + sizeof (inst->response.length) -
8f6a1b53 177 offsetof(SigndResponse, signed_packet), 0);
577aed48
ML
178}
179
180/* ================================================== */
181
182static void
183read_write_socket(int sock_fd, int event, void *anything)
184{
185 SignInstance *inst;
186 uint32_t response_length;
187 int s;
188
189 inst = ARR_GetElement(queue, queue_head);
190
191 if (event == SCH_FILE_OUTPUT) {
192 assert(!IS_QUEUE_EMPTY());
193 assert(inst->sent < inst->request_length);
194
195 if (!inst->sent)
d0dfa1de 196 SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
577aed48 197
2de24cfd
ML
198 s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent,
199 inst->request_length - inst->sent, 0);
577aed48
ML
200
201 if (s < 0) {
577aed48
ML
202 close_socket();
203 return;
204 }
205
577aed48
ML
206 inst->sent += s;
207
208 /* Try again later if the request is not complete yet */
209 if (inst->sent < inst->request_length)
210 return;
211
212 /* Disable output and wait for a response */
2c4c2351 213 SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 0);
577aed48
ML
214 }
215
216 if (event == SCH_FILE_INPUT) {
217 if (IS_QUEUE_EMPTY()) {
f282856c 218 DEBUG_LOG("Unexpected signd response");
577aed48
ML
219 close_socket();
220 return;
221 }
222
223 assert(inst->received < sizeof (inst->response));
2de24cfd
ML
224 s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received,
225 sizeof (inst->response) - inst->received, 0);
577aed48
ML
226
227 if (s <= 0) {
577aed48
ML
228 close_socket();
229 return;
230 }
231
577aed48
ML
232 inst->received += s;
233
234 if (inst->received < sizeof (inst->response.length))
235 return;
236
237 response_length = ntohl(inst->response.length) + sizeof (inst->response.length);
238
239 if (response_length < offsetof(SigndResponse, signed_packet) ||
240 response_length > sizeof (SigndResponse)) {
f282856c 241 DEBUG_LOG("Invalid response length");
577aed48
ML
242 close_socket();
243 return;
244 }
245
246 /* Wait for more data if not complete yet */
247 if (inst->received < response_length)
248 return;
249
250 process_response(inst);
251
252 /* Move the head and enable output for the next packet */
253 queue_head = NEXT_QUEUE_INDEX(queue_head);
254 if (!IS_QUEUE_EMPTY())
2c4c2351 255 SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
577aed48
ML
256 }
257}
258
259/* ================================================== */
260
261void
262NSD_Initialise()
263{
264 sock_fd = INVALID_SOCK_FD;
577aed48
ML
265 enabled = CNF_GetNtpSigndSocket() && CNF_GetNtpSigndSocket()[0];
266
267 if (!enabled)
268 return;
269
270 queue = ARR_CreateInstance(sizeof (SignInstance));
271 ARR_SetSize(queue, MAX_QUEUE_LENGTH);
272 queue_head = queue_tail = 0;
273
f282856c 274 LOG(LOGS_INFO, "MS-SNTP authentication enabled");
577aed48
ML
275}
276
277/* ================================================== */
278
279void
280NSD_Finalise()
281{
282 if (!enabled)
283 return;
284 if (sock_fd != INVALID_SOCK_FD)
285 close_socket();
286 ARR_DestroyInstance(queue);
287}
288
289/* ================================================== */
290
577aed48 291int
cb8660e7
ML
292NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
293 NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
577aed48
ML
294{
295 SignInstance *inst;
296
297 if (!enabled) {
f282856c 298 DEBUG_LOG("signd disabled");
577aed48
ML
299 return 0;
300 }
301
302 if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
f282856c 303 DEBUG_LOG("signd queue full");
577aed48
ML
304 return 0;
305 }
306
cb8660e7 307 if (info->length != NTP_HEADER_LENGTH) {
f282856c 308 DEBUG_LOG("Invalid packet length");
577aed48
ML
309 return 0;
310 }
311
312 if (!open_socket())
313 return 0;
314
315 inst = ARR_GetElement(queue, queue_tail);
316 inst->remote_addr = *remote_addr;
317 inst->local_addr = *local_addr;
318 inst->sent = 0;
319 inst->received = 0;
cb8660e7 320 inst->request_length = offsetof(SigndRequest, packet_to_sign) + info->length;
577aed48
ML
321
322 /* The length field doesn't include itself */
323 inst->request.length = htonl(inst->request_length - sizeof (inst->request.length));
324 inst->request.version = htonl(SIGND_VERSION);
325 inst->request.op = htonl(SIGN_TO_CLIENT);
326 inst->request.packet_id = htons(queue_tail);
327 inst->request._pad = 0;
328 inst->request.key_id = htonl(key_id);
329
cb8660e7 330 memcpy(&inst->request.packet_to_sign, packet, info->length);
577aed48
ML
331
332 /* Enable output if there was no pending request */
333 if (IS_QUEUE_EMPTY())
2c4c2351 334 SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
577aed48
ML
335
336 queue_tail = NEXT_QUEUE_INDEX(queue_tail);
337
f282856c 338 DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
577aed48
ML
339
340 return 1;
341}