]> git.ipfire.org Git - thirdparty/chrony.git/blob - ntp_signd.c
test: use env shebang in all bash scripts
[thirdparty/chrony.git] / ntp_signd.c
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"
37 #include "socket.h"
38 #include "util.h"
39
40 /* Declarations per samba/source4/librpc/idl/ntp_signd.idl */
41
42 #define SIGND_VERSION 0
43
44 typedef 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
52 typedef 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
62 typedef 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
70 typedef struct {
71 NTP_Remote_Address remote_addr;
72 NTP_Local_Address local_addr;
73
74 int sent;
75 int received;
76 int request_length;
77 struct timespec request_ts;
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 */
90 static ARR_Instance queue;
91 static unsigned int queue_head;
92 static unsigned int queue_tail;
93
94 #define INVALID_SOCK_FD (-6)
95
96 /* Unix domain socket connected to ntp_signd */
97 static int sock_fd;
98
99 /* Flag indicating if the MS-SNTP authentication is enabled */
100 static int enabled;
101
102 /* ================================================== */
103
104 static void read_write_socket(int sock_fd, int event, void *anything);
105
106 /* ================================================== */
107
108 static void
109 close_socket(void)
110 {
111 SCH_RemoveFileHandler(sock_fd);
112 SCK_CloseSocket(sock_fd);
113 sock_fd = INVALID_SOCK_FD;
114
115 /* Empty the queue */
116 queue_head = queue_tail = 0;
117 }
118
119 /* ================================================== */
120
121 static int
122 open_socket(void)
123 {
124 char path[PATH_MAX];
125
126 if (sock_fd != INVALID_SOCK_FD)
127 return 1;
128
129 if (snprintf(path, sizeof (path), "%s/socket", CNF_GetNtpSigndSocket()) >= sizeof (path)) {
130 DEBUG_LOG("signd socket path too long");
131 return 0;
132 }
133
134 sock_fd = SCK_OpenUnixStreamSocket(path, NULL, 0);
135 if (sock_fd < 0) {
136 sock_fd = INVALID_SOCK_FD;
137 return 0;
138 }
139
140 SCH_AddFileHandler(sock_fd, SCH_FILE_INPUT, read_write_socket, NULL);
141
142 return 1;
143 }
144
145 /* ================================================== */
146
147 static void
148 process_response(SignInstance *inst)
149 {
150 struct timespec ts;
151 double delay;
152
153 if (ntohs(inst->request.packet_id) != ntohl(inst->response.packet_id)) {
154 DEBUG_LOG("Invalid response ID");
155 return;
156 }
157
158 if (ntohl(inst->response.op) != SIGNING_SUCCESS) {
159 DEBUG_LOG("Signing failed");
160 return;
161 }
162
163 /* Check if the file descriptor is still valid */
164 if (!NIO_IsServerSocket(inst->local_addr.sock_fd)) {
165 DEBUG_LOG("Invalid NTP socket");
166 return;
167 }
168
169 SCH_GetLastEventTime(NULL, NULL, &ts);
170 delay = UTI_DiffTimespecsToDouble(&ts, &inst->request_ts);
171
172 DEBUG_LOG("Signing succeeded (delay %f)", delay);
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) -
177 offsetof(SigndResponse, signed_packet), 0);
178 }
179
180 /* ================================================== */
181
182 static void
183 read_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)
196 SCH_GetLastEventTime(NULL, NULL, &inst->request_ts);
197
198 s = SCK_Send(sock_fd, (char *)&inst->request + inst->sent,
199 inst->request_length - inst->sent, 0);
200
201 if (s < 0) {
202 close_socket();
203 return;
204 }
205
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 */
213 SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 0);
214 }
215
216 if (event == SCH_FILE_INPUT) {
217 if (IS_QUEUE_EMPTY()) {
218 DEBUG_LOG("Unexpected signd response");
219 close_socket();
220 return;
221 }
222
223 assert(inst->received < sizeof (inst->response));
224 s = SCK_Receive(sock_fd, (char *)&inst->response + inst->received,
225 sizeof (inst->response) - inst->received, 0);
226
227 if (s <= 0) {
228 close_socket();
229 return;
230 }
231
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)) {
241 DEBUG_LOG("Invalid response length");
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())
255 SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
256 }
257 }
258
259 /* ================================================== */
260
261 void
262 NSD_Initialise()
263 {
264 sock_fd = INVALID_SOCK_FD;
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
274 LOG(LOGS_INFO, "MS-SNTP authentication enabled");
275 }
276
277 /* ================================================== */
278
279 void
280 NSD_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
291 int
292 NSD_SignAndSendPacket(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info,
293 NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr)
294 {
295 SignInstance *inst;
296
297 if (!enabled) {
298 DEBUG_LOG("signd disabled");
299 return 0;
300 }
301
302 if (queue_head == NEXT_QUEUE_INDEX(queue_tail)) {
303 DEBUG_LOG("signd queue full");
304 return 0;
305 }
306
307 if (info->length != NTP_HEADER_LENGTH) {
308 DEBUG_LOG("Invalid packet length");
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;
320 inst->request_length = offsetof(SigndRequest, packet_to_sign) + info->length;
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
330 memcpy(&inst->request.packet_to_sign, packet, info->length);
331
332 /* Enable output if there was no pending request */
333 if (IS_QUEUE_EMPTY())
334 SCH_SetFileHandlerEvent(sock_fd, SCH_FILE_OUTPUT, 1);
335
336 queue_tail = NEXT_QUEUE_INDEX(queue_tail);
337
338 DEBUG_LOG("Packet added to signd queue (%u:%u)", queue_head, queue_tail);
339
340 return 1;
341 }