]> git.ipfire.org Git - thirdparty/chrony.git/blob - nts_ke_client.c
conf: rework allow/deny parser
[thirdparty/chrony.git] / nts_ke_client.c
1 /*
2 chronyd/chronyc - Programs for keeping computer clocks accurate.
3
4 **********************************************************************
5 * Copyright (C) Miroslav Lichvar 2020-2021
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 NTS-KE client
25 */
26
27 #include "config.h"
28
29 #include "sysincl.h"
30
31 #include "nts_ke_client.h"
32
33 #include "conf.h"
34 #include "logging.h"
35 #include "memory.h"
36 #include "nameserv_async.h"
37 #include "nts_ke_session.h"
38 #include "siv.h"
39 #include "socket.h"
40 #include "util.h"
41
42 #define CLIENT_TIMEOUT 16.0
43
44 struct NKC_Instance_Record {
45 char *name;
46 IPSockAddr address;
47 NKSN_Credentials credentials;
48 NKSN_Instance session;
49 int destroying;
50 int got_response;
51 int resolving_name;
52
53 NKE_Context context;
54 NKE_Cookie cookies[NKE_MAX_COOKIES];
55 int num_cookies;
56 char server_name[NKE_MAX_RECORD_BODY_LENGTH + 2];
57 IPSockAddr ntp_address;
58 };
59
60 /* ================================================== */
61
62 static NKSN_Credentials default_credentials = NULL;
63 static int default_credentials_refs = 0;
64
65 /* ================================================== */
66
67 static void
68 name_resolve_handler(DNS_Status status, int n_addrs, IPAddr *ip_addrs, void *arg)
69 {
70 NKC_Instance inst = arg;
71 int i;
72
73 inst->resolving_name = 0;
74
75 if (inst->destroying) {
76 Free(inst);
77 return;
78 }
79
80 if (status != DNS_Success || n_addrs < 1) {
81 LOG(LOGS_ERR, "Could not resolve NTP server %s from %s", inst->server_name, inst->name);
82 /* Force restart */
83 inst->got_response = 0;
84 return;
85 }
86
87 inst->ntp_address.ip_addr = ip_addrs[0];
88
89 /* Prefer an address in the same family as the NTS-KE server */
90 for (i = 0; i < n_addrs; i++) {
91 DEBUG_LOG("%s resolved to %s", inst->server_name, UTI_IPToString(&ip_addrs[i]));
92 if (ip_addrs[i].family == inst->address.ip_addr.family) {
93 inst->ntp_address.ip_addr = ip_addrs[i];
94 break;
95 }
96 }
97 }
98
99 /* ================================================== */
100
101 static int
102 prepare_request(NKC_Instance inst)
103 {
104 NKSN_Instance session = inst->session;
105 uint16_t datum;
106
107 NKSN_BeginMessage(session);
108
109 datum = htons(NKE_NEXT_PROTOCOL_NTPV4);
110 if (!NKSN_AddRecord(session, 1, NKE_RECORD_NEXT_PROTOCOL, &datum, sizeof (datum)))
111 return 0;
112
113 datum = htons(AEAD_AES_SIV_CMAC_256);
114 if (!NKSN_AddRecord(session, 1, NKE_RECORD_AEAD_ALGORITHM, &datum, sizeof (datum)))
115 return 0;
116
117 if (!NKSN_EndMessage(session))
118 return 0;
119
120 return 1;
121 }
122
123 /* ================================================== */
124
125 static int
126 process_response(NKC_Instance inst)
127 {
128 int next_protocol = -1, aead_algorithm = -1, error = 0;
129 int i, critical, type, length;
130 uint16_t data[NKE_MAX_RECORD_BODY_LENGTH / sizeof (uint16_t)];
131
132 assert(NKE_MAX_COOKIE_LENGTH <= NKE_MAX_RECORD_BODY_LENGTH);
133 assert(sizeof (data) % sizeof (uint16_t) == 0);
134 assert(sizeof (uint16_t) == 2);
135
136 inst->num_cookies = 0;
137 inst->ntp_address.ip_addr.family = IPADDR_UNSPEC;
138 inst->ntp_address.port = 0;
139 inst->server_name[0] = '\0';
140
141 while (!error) {
142 if (!NKSN_GetRecord(inst->session, &critical, &type, &length, &data, sizeof (data)))
143 break;
144
145 if (length > sizeof (data)) {
146 DEBUG_LOG("Record too long type=%d length=%d critical=%d", type, length, critical);
147 if (critical)
148 error = 1;
149 continue;
150 }
151
152 switch (type) {
153 case NKE_RECORD_NEXT_PROTOCOL:
154 if (!critical || length != 2 || ntohs(data[0]) != NKE_NEXT_PROTOCOL_NTPV4) {
155 DEBUG_LOG("Unexpected NTS-KE next protocol");
156 error = 1;
157 break;
158 }
159 next_protocol = NKE_NEXT_PROTOCOL_NTPV4;
160 break;
161 case NKE_RECORD_AEAD_ALGORITHM:
162 if (length != 2 || ntohs(data[0]) != AEAD_AES_SIV_CMAC_256) {
163 DEBUG_LOG("Unexpected NTS-KE AEAD algorithm");
164 error = 1;
165 break;
166 }
167 aead_algorithm = AEAD_AES_SIV_CMAC_256;
168 inst->context.algorithm = aead_algorithm;
169 break;
170 case NKE_RECORD_ERROR:
171 if (length == 2)
172 DEBUG_LOG("NTS-KE error %d", ntohs(data[0]));
173 error = 1;
174 break;
175 case NKE_RECORD_WARNING:
176 if (length == 2)
177 DEBUG_LOG("NTS-KE warning %d", ntohs(data[0]));
178 error = 1;
179 break;
180 case NKE_RECORD_COOKIE:
181 DEBUG_LOG("Got cookie length=%d", length);
182
183 if (length < 1 || length > NKE_MAX_COOKIE_LENGTH || length % 4 != 0 ||
184 inst->num_cookies >= NKE_MAX_COOKIES) {
185 DEBUG_LOG("Unexpected length/cookie");
186 break;
187 }
188
189 assert(NKE_MAX_COOKIE_LENGTH == sizeof (inst->cookies[inst->num_cookies].cookie));
190 assert(NKE_MAX_COOKIES == sizeof (inst->cookies) /
191 sizeof (inst->cookies[inst->num_cookies]));
192 inst->cookies[inst->num_cookies].length = length;
193 memcpy(inst->cookies[inst->num_cookies].cookie, data, length);
194
195 inst->num_cookies++;
196 break;
197 case NKE_RECORD_NTPV4_SERVER_NEGOTIATION:
198 if (length < 1 || length >= sizeof (inst->server_name)) {
199 DEBUG_LOG("Invalid server name");
200 error = 1;
201 break;
202 }
203
204 memcpy(inst->server_name, data, length);
205 inst->server_name[length] = '\0';
206
207 /* Make sure the name is printable and has no spaces */
208 for (i = 0; i < length && isgraph((unsigned char)inst->server_name[i]); i++)
209 ;
210 if (i != length) {
211 DEBUG_LOG("Invalid server name");
212 error = 1;
213 break;
214 }
215
216 DEBUG_LOG("Negotiated server %s", inst->server_name);
217 break;
218 case NKE_RECORD_NTPV4_PORT_NEGOTIATION:
219 if (length != 2) {
220 DEBUG_LOG("Invalid port");
221 error = 1;
222 break;
223 }
224 inst->ntp_address.port = ntohs(data[0]);
225 DEBUG_LOG("Negotiated port %d", inst->ntp_address.port);
226 break;
227 default:
228 DEBUG_LOG("Unknown record type=%d length=%d critical=%d", type, length, critical);
229 if (critical)
230 error = 1;
231 }
232 }
233
234 DEBUG_LOG("NTS-KE response: error=%d next=%d aead=%d",
235 error, next_protocol, aead_algorithm);
236
237 if (error || inst->num_cookies == 0 ||
238 next_protocol != NKE_NEXT_PROTOCOL_NTPV4 ||
239 aead_algorithm != AEAD_AES_SIV_CMAC_256)
240 return 0;
241
242 return 1;
243 }
244
245 /* ================================================== */
246
247 static int
248 handle_message(void *arg)
249 {
250 NKC_Instance inst = arg;
251
252 if (!process_response(inst)) {
253 LOG(LOGS_ERR, "Received invalid NTS-KE response from %s", inst->name);
254 return 0;
255 }
256
257 if (!NKSN_GetKeys(inst->session, inst->context.algorithm,
258 &inst->context.c2s, &inst->context.s2c))
259 return 0;
260
261 if (inst->server_name[0] != '\0') {
262 if (inst->resolving_name)
263 return 0;
264 if (!UTI_StringToIP(inst->server_name, &inst->ntp_address.ip_addr)) {
265 int length = strlen(inst->server_name);
266
267 /* Add a trailing dot if not present to force the name to be
268 resolved as a fully qualified domain name */
269 if (length < 1 || length + 1 >= sizeof (inst->server_name))
270 return 0;
271 if (inst->server_name[length - 1] != '.') {
272 inst->server_name[length] = '.';
273 inst->server_name[length + 1] = '\0';
274 }
275
276 DNS_Name2IPAddressAsync(inst->server_name, name_resolve_handler, inst);
277 inst->resolving_name = 1;
278 }
279 }
280
281 inst->got_response = 1;
282
283 return 1;
284 }
285
286 /* ================================================== */
287
288 NKC_Instance
289 NKC_CreateInstance(IPSockAddr *address, const char *name, uint32_t cert_set)
290 {
291 const char **trusted_certs;
292 uint32_t *certs_ids;
293 NKC_Instance inst;
294 int n_certs;
295
296 inst = MallocNew(struct NKC_Instance_Record);
297
298 inst->address = *address;
299 inst->name = Strdup(name);
300 inst->session = NKSN_CreateInstance(0, inst->name, handle_message, inst);
301 inst->resolving_name = 0;
302 inst->destroying = 0;
303 inst->got_response = 0;
304
305 n_certs = CNF_GetNtsTrustedCertsPaths(&trusted_certs, &certs_ids);
306
307 /* Share the credentials among clients using the default set of trusted
308 certificates, which likely contains most certificates */
309 if (cert_set == 0) {
310 if (!default_credentials)
311 default_credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
312 n_certs, cert_set);
313 inst->credentials = default_credentials;
314 if (default_credentials)
315 default_credentials_refs++;
316 } else {
317 inst->credentials = NKSN_CreateClientCertCredentials(trusted_certs, certs_ids,
318 n_certs, cert_set);
319 }
320
321 return inst;
322 }
323
324 /* ================================================== */
325
326 void
327 NKC_DestroyInstance(NKC_Instance inst)
328 {
329 NKSN_DestroyInstance(inst->session);
330
331 Free(inst->name);
332
333 if (inst->credentials) {
334 if (inst->credentials == default_credentials) {
335 default_credentials_refs--;
336 if (default_credentials_refs <= 0) {
337 NKSN_DestroyCertCredentials(default_credentials);
338 default_credentials = NULL;
339 }
340 } else {
341 NKSN_DestroyCertCredentials(inst->credentials);
342 }
343 }
344
345 /* If the asynchronous resolver is running, let the handler free
346 the instance later */
347 if (inst->resolving_name) {
348 inst->destroying = 1;
349 return;
350 }
351
352 Free(inst);
353 }
354
355 /* ================================================== */
356
357 int
358 NKC_Start(NKC_Instance inst)
359 {
360 IPSockAddr local_addr;
361 char label[512], *iface;
362 int sock_fd;
363
364 assert(!NKC_IsActive(inst));
365
366 inst->got_response = 0;
367
368 if (!inst->credentials) {
369 DEBUG_LOG("Missing client credentials");
370 return 0;
371 }
372
373 /* Follow the bindacqaddress and bindacqdevice settings */
374 CNF_GetBindAcquisitionAddress(inst->address.ip_addr.family, &local_addr.ip_addr);
375 local_addr.port = 0;
376 iface = CNF_GetBindAcquisitionInterface();
377
378 /* Make a label containing both the address and name of the server */
379 if (snprintf(label, sizeof (label), "%s (%s)",
380 UTI_IPSockAddrToString(&inst->address), inst->name) >= sizeof (label))
381 ;
382
383 sock_fd = SCK_OpenTcpSocket(&inst->address, &local_addr, iface, 0);
384 if (sock_fd < 0) {
385 LOG(LOGS_ERR, "Could not connect to %s", label);
386 return 0;
387 }
388
389 /* Start an NTS-KE session */
390 if (!NKSN_StartSession(inst->session, sock_fd, label, inst->credentials, CLIENT_TIMEOUT)) {
391 SCK_CloseSocket(sock_fd);
392 return 0;
393 }
394
395 /* Send a request */
396 if (!prepare_request(inst)) {
397 DEBUG_LOG("Could not prepare NTS-KE request");
398 NKSN_StopSession(inst->session);
399 return 0;
400 }
401
402 return 1;
403 }
404
405 /* ================================================== */
406
407 int
408 NKC_IsActive(NKC_Instance inst)
409 {
410 return !NKSN_IsStopped(inst->session) || inst->resolving_name;
411 }
412
413 /* ================================================== */
414
415 int
416 NKC_GetNtsData(NKC_Instance inst, NKE_Context *context,
417 NKE_Cookie *cookies, int *num_cookies, int max_cookies,
418 IPSockAddr *ntp_address)
419 {
420 int i;
421
422 if (!inst->got_response || inst->resolving_name)
423 return 0;
424
425 *context = inst->context;
426
427 for (i = 0; i < inst->num_cookies && i < max_cookies; i++)
428 cookies[i] = inst->cookies[i];
429 *num_cookies = i;
430
431 *ntp_address = inst->ntp_address;
432
433 return 1;
434 }
435
436 /* ================================================== */
437
438 int
439 NKC_GetRetryFactor(NKC_Instance inst)
440 {
441 return NKSN_GetRetryFactor(inst->session);
442 }