]>
Commit | Line | Data |
---|---|---|
ca28dbd2 ML |
1 | /* |
2 | chronyd/chronyc - Programs for keeping computer clocks accurate. | |
3 | ||
4 | ********************************************************************** | |
3916c336 | 5 | * Copyright (C) Miroslav Lichvar 2019-2020 |
ca28dbd2 ML |
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 | NTP authentication | |
25 | */ | |
26 | ||
27 | #include "config.h" | |
28 | ||
29 | #include "sysincl.h" | |
30 | ||
31 | #include "keys.h" | |
32 | #include "logging.h" | |
33 | #include "memory.h" | |
34 | #include "ntp_auth.h" | |
35 | #include "ntp_signd.h" | |
c4150872 ML |
36 | #include "nts_ntp.h" |
37 | #include "nts_ntp_client.h" | |
38 | #include "nts_ntp_server.h" | |
ca28dbd2 ML |
39 | #include "srcparams.h" |
40 | #include "util.h" | |
41 | ||
42 | /* Structure to hold authentication configuration and state */ | |
43 | ||
44 | struct NAU_Instance_Record { | |
45 | NTP_AuthMode mode; /* Authentication mode of NTP packets */ | |
46 | uint32_t key_id; /* Identifier of a symmetric key */ | |
c4150872 | 47 | NNC_Instance nts; /* Client NTS state */ |
ca28dbd2 ML |
48 | }; |
49 | ||
50 | /* ================================================== */ | |
51 | ||
52 | static int | |
53 | generate_symmetric_auth(uint32_t key_id, NTP_Packet *packet, NTP_PacketInfo *info) | |
54 | { | |
55 | int auth_len, max_auth_len; | |
56 | ||
0e51552d ML |
57 | if (info->length + NTP_MIN_MAC_LENGTH > sizeof (*packet)) { |
58 | DEBUG_LOG("Packet too long"); | |
59 | return 0; | |
60 | } | |
61 | ||
ca28dbd2 ML |
62 | /* Truncate long MACs in NTPv4 packets to allow deterministic parsing |
63 | of extension fields (RFC 7822) */ | |
64 | max_auth_len = (info->version == 4 ? NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH) - 4; | |
0e51552d | 65 | max_auth_len = MIN(max_auth_len, sizeof (*packet) - info->length - 4); |
ca28dbd2 | 66 | |
d93aa10b | 67 | auth_len = KEY_GenerateAuth(key_id, packet, info->length, |
ca28dbd2 | 68 | (unsigned char *)packet + info->length + 4, max_auth_len); |
0e51552d | 69 | if (auth_len < NTP_MIN_MAC_LENGTH - 4) { |
ca28dbd2 ML |
70 | DEBUG_LOG("Could not generate auth data with key %"PRIu32, key_id); |
71 | return 0; | |
72 | } | |
73 | ||
74 | *(uint32_t *)((unsigned char *)packet + info->length) = htonl(key_id); | |
0e51552d ML |
75 | |
76 | info->auth.mac.start = info->length; | |
77 | info->auth.mac.length = 4 + auth_len; | |
78 | info->auth.mac.key_id = key_id; | |
79 | info->length += info->auth.mac.length; | |
ca28dbd2 ML |
80 | |
81 | return 1; | |
82 | } | |
83 | ||
84 | /* ================================================== */ | |
85 | ||
86 | static int | |
87 | check_symmetric_auth(NTP_Packet *packet, NTP_PacketInfo *info) | |
88 | { | |
89 | int trunc_len; | |
90 | ||
91 | if (info->auth.mac.length < NTP_MIN_MAC_LENGTH) | |
92 | return 0; | |
93 | ||
94 | trunc_len = info->version == 4 && info->auth.mac.length <= NTP_MAX_V4_MAC_LENGTH ? | |
95 | NTP_MAX_V4_MAC_LENGTH : NTP_MAX_MAC_LENGTH; | |
96 | ||
d93aa10b | 97 | if (!KEY_CheckAuth(info->auth.mac.key_id, packet, info->auth.mac.start, |
ca28dbd2 ML |
98 | (unsigned char *)packet + info->auth.mac.start + 4, |
99 | info->auth.mac.length - 4, trunc_len - 4)) | |
100 | return 0; | |
101 | ||
102 | return 1; | |
103 | } | |
104 | ||
105 | /* ================================================== */ | |
106 | ||
ca28dbd2 ML |
107 | static NAU_Instance |
108 | create_instance(NTP_AuthMode mode) | |
109 | { | |
110 | NAU_Instance instance; | |
111 | ||
112 | instance = MallocNew(struct NAU_Instance_Record); | |
113 | instance->mode = mode; | |
114 | instance->key_id = INACTIVE_AUTHKEY; | |
c4150872 | 115 | instance->nts = NULL; |
ca28dbd2 ML |
116 | |
117 | assert(sizeof (instance->key_id) == 4); | |
118 | ||
119 | return instance; | |
120 | } | |
121 | ||
122 | /* ================================================== */ | |
123 | ||
124 | NAU_Instance | |
125 | NAU_CreateNoneInstance(void) | |
126 | { | |
46cac4e2 | 127 | return create_instance(NTP_AUTH_NONE); |
ca28dbd2 ML |
128 | } |
129 | ||
130 | /* ================================================== */ | |
131 | ||
132 | NAU_Instance | |
133 | NAU_CreateSymmetricInstance(uint32_t key_id) | |
134 | { | |
46cac4e2 | 135 | NAU_Instance instance = create_instance(NTP_AUTH_SYMMETRIC); |
ca28dbd2 ML |
136 | |
137 | instance->key_id = key_id; | |
138 | ||
139 | if (!KEY_KeyKnown(key_id)) | |
140 | LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "missing"); | |
141 | else if (!KEY_CheckKeyLength(key_id)) | |
142 | LOG(LOGS_WARN, "Key %"PRIu32" is %s", key_id, "too short"); | |
143 | ||
144 | return instance; | |
145 | } | |
146 | ||
147 | /* ================================================== */ | |
148 | ||
c4150872 | 149 | NAU_Instance |
6615bb1b ML |
150 | NAU_CreateNtsInstance(IPSockAddr *nts_address, const char *name, uint32_t cert_set, |
151 | uint16_t ntp_port) | |
c4150872 ML |
152 | { |
153 | NAU_Instance instance = create_instance(NTP_AUTH_NTS); | |
154 | ||
6615bb1b | 155 | instance->nts = NNC_CreateInstance(nts_address, name, cert_set, ntp_port); |
c4150872 ML |
156 | |
157 | return instance; | |
158 | } | |
159 | ||
160 | /* ================================================== */ | |
161 | ||
ca28dbd2 ML |
162 | void |
163 | NAU_DestroyInstance(NAU_Instance instance) | |
164 | { | |
59ad433b | 165 | if (instance->mode == NTP_AUTH_NTS) |
c4150872 | 166 | NNC_DestroyInstance(instance->nts); |
ca28dbd2 ML |
167 | Free(instance); |
168 | } | |
169 | ||
170 | /* ================================================== */ | |
171 | ||
172 | int | |
173 | NAU_IsAuthEnabled(NAU_Instance instance) | |
174 | { | |
46cac4e2 | 175 | return instance->mode != NTP_AUTH_NONE; |
ca28dbd2 ML |
176 | } |
177 | ||
178 | /* ================================================== */ | |
179 | ||
180 | int | |
181 | NAU_GetSuggestedNtpVersion(NAU_Instance instance) | |
182 | { | |
183 | /* If the MAC in NTPv4 packets would be truncated, prefer NTPv3 for | |
184 | compatibility with older chronyd servers */ | |
46cac4e2 | 185 | if (instance->mode == NTP_AUTH_SYMMETRIC && |
ca28dbd2 ML |
186 | KEY_GetAuthLength(instance->key_id) + sizeof (instance->key_id) > NTP_MAX_V4_MAC_LENGTH) |
187 | return 3; | |
188 | ||
189 | return NTP_VERSION; | |
190 | } | |
191 | ||
192 | /* ================================================== */ | |
193 | ||
194 | int | |
195 | NAU_PrepareRequestAuth(NAU_Instance instance) | |
196 | { | |
197 | switch (instance->mode) { | |
c4150872 ML |
198 | case NTP_AUTH_NTS: |
199 | if (!NNC_PrepareForAuth(instance->nts)) | |
200 | return 0; | |
201 | break; | |
ca28dbd2 ML |
202 | default: |
203 | break; | |
204 | } | |
205 | ||
206 | return 1; | |
207 | } | |
208 | ||
209 | /* ================================================== */ | |
210 | ||
ca28dbd2 ML |
211 | int |
212 | NAU_GenerateRequestAuth(NAU_Instance instance, NTP_Packet *request, NTP_PacketInfo *info) | |
213 | { | |
214 | switch (instance->mode) { | |
46cac4e2 | 215 | case NTP_AUTH_NONE: |
ca28dbd2 | 216 | break; |
46cac4e2 | 217 | case NTP_AUTH_SYMMETRIC: |
ca28dbd2 ML |
218 | if (!generate_symmetric_auth(instance->key_id, request, info)) |
219 | return 0; | |
220 | break; | |
c4150872 ML |
221 | case NTP_AUTH_NTS: |
222 | if (!NNC_GenerateRequestAuth(instance->nts, request, info)) | |
223 | return 0; | |
224 | break; | |
ca28dbd2 ML |
225 | default: |
226 | assert(0); | |
227 | } | |
228 | ||
0e51552d ML |
229 | info->auth.mode = instance->mode; |
230 | ||
ca28dbd2 ML |
231 | return 1; |
232 | } | |
233 | ||
234 | /* ================================================== */ | |
235 | ||
236 | int | |
aca1daf7 | 237 | NAU_CheckRequestAuth(NTP_Packet *request, NTP_PacketInfo *info, uint32_t *kod) |
ca28dbd2 | 238 | { |
aca1daf7 ML |
239 | *kod = 0; |
240 | ||
ca28dbd2 | 241 | switch (info->auth.mode) { |
46cac4e2 | 242 | case NTP_AUTH_NONE: |
ca28dbd2 | 243 | break; |
46cac4e2 | 244 | case NTP_AUTH_SYMMETRIC: |
ca28dbd2 ML |
245 | if (!check_symmetric_auth(request, info)) |
246 | return 0; | |
247 | break; | |
46cac4e2 | 248 | case NTP_AUTH_MSSNTP: |
ca28dbd2 ML |
249 | /* MS-SNTP requests are not authenticated */ |
250 | break; | |
0e51552d ML |
251 | case NTP_AUTH_MSSNTP_EXT: |
252 | /* Not supported yet */ | |
253 | return 0; | |
c4150872 ML |
254 | case NTP_AUTH_NTS: |
255 | if (!NNS_CheckRequestAuth(request, info, kod)) | |
256 | return 0; | |
257 | break; | |
ca28dbd2 ML |
258 | default: |
259 | return 0; | |
260 | } | |
261 | ||
262 | return 1; | |
263 | } | |
264 | ||
265 | /* ================================================== */ | |
ca28dbd2 ML |
266 | |
267 | int | |
268 | NAU_GenerateResponseAuth(NTP_Packet *request, NTP_PacketInfo *request_info, | |
269 | NTP_Packet *response, NTP_PacketInfo *response_info, | |
aca1daf7 ML |
270 | NTP_Remote_Address *remote_addr, NTP_Local_Address *local_addr, |
271 | uint32_t kod) | |
ca28dbd2 ML |
272 | { |
273 | switch (request_info->auth.mode) { | |
46cac4e2 | 274 | case NTP_AUTH_NONE: |
ca28dbd2 | 275 | break; |
46cac4e2 | 276 | case NTP_AUTH_SYMMETRIC: |
ca28dbd2 ML |
277 | if (!generate_symmetric_auth(request_info->auth.mac.key_id, response, response_info)) |
278 | return 0; | |
279 | break; | |
46cac4e2 | 280 | case NTP_AUTH_MSSNTP: |
ca28dbd2 ML |
281 | /* Sign the packet asynchronously by ntp_signd */ |
282 | if (!NSD_SignAndSendPacket(request_info->auth.mac.key_id, response, response_info, | |
283 | remote_addr, local_addr)) | |
284 | return 0; | |
285 | /* Don't send the original packet */ | |
286 | return 0; | |
c4150872 ML |
287 | case NTP_AUTH_NTS: |
288 | if (!NNS_GenerateResponseAuth(request, request_info, response, response_info, kod)) | |
289 | return 0; | |
290 | break; | |
ca28dbd2 ML |
291 | default: |
292 | DEBUG_LOG("Could not authenticate response auth_mode=%d", (int)request_info->auth.mode); | |
293 | return 0; | |
294 | } | |
295 | ||
0e51552d ML |
296 | response_info->auth.mode = request_info->auth.mode; |
297 | ||
ca28dbd2 ML |
298 | return 1; |
299 | } | |
300 | ||
301 | /* ================================================== */ | |
302 | ||
303 | int | |
304 | NAU_CheckResponseAuth(NAU_Instance instance, NTP_Packet *response, NTP_PacketInfo *info) | |
305 | { | |
ca28dbd2 ML |
306 | /* The authentication must match the expected mode */ |
307 | if (info->auth.mode != instance->mode) | |
308 | return 0; | |
309 | ||
310 | switch (info->auth.mode) { | |
46cac4e2 | 311 | case NTP_AUTH_NONE: |
ca28dbd2 | 312 | break; |
46cac4e2 | 313 | case NTP_AUTH_SYMMETRIC: |
ca28dbd2 ML |
314 | /* Check if it is authenticated with the specified key */ |
315 | if (info->auth.mac.key_id != instance->key_id) | |
316 | return 0; | |
317 | /* and that the MAC is valid */ | |
318 | if (!check_symmetric_auth(response, info)) | |
319 | return 0; | |
320 | break; | |
c4150872 ML |
321 | case NTP_AUTH_NTS: |
322 | if (!NNC_CheckResponseAuth(instance->nts, response, info)) | |
323 | return 0; | |
324 | break; | |
ca28dbd2 ML |
325 | default: |
326 | return 0; | |
327 | } | |
328 | ||
329 | return 1; | |
330 | } | |
86d29221 ML |
331 | |
332 | /* ================================================== */ | |
333 | ||
334 | void | |
335 | NAU_ChangeAddress(NAU_Instance instance, IPAddr *address) | |
336 | { | |
337 | switch (instance->mode) { | |
338 | case NTP_AUTH_NONE: | |
339 | case NTP_AUTH_SYMMETRIC: | |
340 | break; | |
c4150872 ML |
341 | case NTP_AUTH_NTS: |
342 | NNC_ChangeAddress(instance->nts, address); | |
343 | break; | |
86d29221 ML |
344 | default: |
345 | assert(0); | |
346 | } | |
347 | } | |
d690faeb ML |
348 | |
349 | /* ================================================== */ | |
350 | ||
351 | void | |
352 | NAU_DumpData(NAU_Instance instance) | |
353 | { | |
354 | switch (instance->mode) { | |
355 | case NTP_AUTH_NTS: | |
356 | NNC_DumpData(instance->nts); | |
357 | break; | |
358 | default: | |
359 | break; | |
360 | } | |
361 | } | |
79c7384e ML |
362 | |
363 | /* ================================================== */ | |
364 | ||
365 | void | |
366 | NAU_GetReport(NAU_Instance instance, RPT_AuthReport *report) | |
367 | { | |
368 | memset(report, 0, sizeof (*report)); | |
369 | ||
370 | report->mode = instance->mode; | |
371 | report->last_ke_ago = -1; | |
372 | ||
373 | switch (instance->mode) { | |
374 | case NTP_AUTH_NONE: | |
375 | break; | |
376 | case NTP_AUTH_SYMMETRIC: | |
377 | report->key_id = instance->key_id; | |
378 | KEY_GetKeyInfo(instance->key_id, &report->key_type, &report->key_length); | |
379 | break; | |
380 | case NTP_AUTH_NTS: | |
381 | NNC_GetReport(instance->nts, report); | |
382 | break; | |
383 | default: | |
384 | assert(0); | |
385 | } | |
386 | } |