Coverage Report

Created: 2025-07-18 06:53

/src/ntpsec/ntpd/nts_extens.c
Line
Count
Source (jump to first uncovered line)
1
/*
2
 * ntp_extens.c - Network Time Protocol (NTP) extension processing
3
 * Copyright the NTPsec project contributors
4
 * SPDX-License-Identifier: BSD-2-Clause
5
 *
6
 * NB: This module is working with the wire format packet.
7
 *     It must do byte swapping.
8
 *
9
 * We carefully arrange things so that no padding is necessary.
10
 *
11
 * This is only called by the main ntpd thread, so we don't need
12
 * a lock to protect wire_ctx.
13
 */
14
15
#include "config.h"
16
17
#include <stdbool.h>
18
#include <stdint.h>
19
#include <string.h>
20
21
#include <aes_siv.h>
22
23
#include "ntp_stdlib.h"
24
#include "ntp.h"
25
#include "ntpd.h"
26
#include "nts.h"
27
#include "nts2.h"
28
29
0
#define NONCE_LENGTH 16
30
0
#define CMAC_LENGTH 16
31
32
0
#define NTP_EX_HDR_LNG 4
33
0
#define NTP_EX_U16_LNG 2
34
35
enum NtpExtFieldType {
36
  Unique_Identifier = 0x104,
37
  NTS_Cookie = 0x204,
38
  NTS_Cookie_Placeholder = 0x304,
39
  NTS_AEEF = 0x404 /* Authenticated and Encrypted Extension Fields */
40
};
41
42
/* This is only called by the main thread so we don't need a lock. */
43
AES_SIV_CTX* wire_ctx = NULL;
44
45
46
0
bool extens_init(void) {
47
0
  wire_ctx = AES_SIV_CTX_new();
48
0
  if (NULL == wire_ctx) {
49
0
    msyslog(LOG_ERR, "NTS: Can't init wire_ctx");
50
0
    exit(1);
51
0
  }
52
0
  return true;
53
0
}
54
55
0
int extens_client_send(struct peer *peer, struct pkt *xpkt) {
56
0
  struct BufCtl_t buf;
57
0
  int used, adlength, idx;
58
0
  size_t left;
59
0
  uint8_t *nonce, *packet;
60
0
  bool ok;
61
62
0
  packet = (uint8_t*)xpkt;
63
0
  buf.next = xpkt->exten;
64
0
  buf.left = MAX_EXT_LEN;
65
66
  /* UID */
67
0
  ntp_RAND_bytes(peer->nts_state.UID, NTS_UID_LENGTH);
68
0
  ex_append_record_bytes(&buf, Unique_Identifier,
69
0
             peer->nts_state.UID, NTS_UID_LENGTH);
70
71
  /* cookie */
72
0
  idx = peer->nts_state.readIdx++;
73
0
  ex_append_record_bytes(&buf, NTS_Cookie,
74
0
             peer->nts_state.cookies[idx], peer->nts_state.cookielen);
75
0
  peer->nts_state.readIdx = peer->nts_state.readIdx % NTS_MAX_COOKIES;
76
0
  peer->nts_state.count--;
77
78
  /* Need more cookies? */
79
0
  for (int i=peer->nts_state.count+1; i<NTS_MAX_COOKIES; i++) {
80
    /* WARN: This may get too big for the MTU. */
81
0
    ex_append_header(&buf, NTS_Cookie_Placeholder, peer->nts_state.cookielen);
82
0
    memset(buf.next, 0, peer->nts_state.cookielen);
83
0
    buf.next += peer->nts_state.cookielen;
84
0
    buf.left -= peer->nts_state.cookielen;
85
0
  }
86
87
  /* AEAD */
88
0
  adlength = buf.next-packet;
89
0
  ex_append_header(&buf, NTS_AEEF, NTP_EX_U16_LNG*2+NONCE_LENGTH+CMAC_LENGTH);
90
0
  append_uint16(&buf, NONCE_LENGTH);
91
0
  append_uint16(&buf, CMAC_LENGTH);
92
0
  nonce = buf.next;
93
0
  ntp_RAND_bytes(nonce, NONCE_LENGTH);
94
0
  buf.next += NONCE_LENGTH;
95
0
  buf.left -= NONCE_LENGTH;
96
0
  left = buf.left;
97
0
  ok = AES_SIV_Encrypt(wire_ctx,
98
0
           buf.next, &left,   /* left: in: max out length, out: length used */
99
0
           peer->nts_state.c2s, peer->nts_state.keylen,
100
0
           nonce, NONCE_LENGTH,
101
0
           NULL, 0,           /* no plain/cipher text */
102
0
           packet, adlength);
103
0
  if (!ok) {
104
0
    msyslog(LOG_ERR, "NTS: extens_client_send - Error from AES_SIV_Encrypt");
105
    /* I don't think this should happen,
106
     * so crash rather than work incorrectly.
107
     * Hal, 2019-Feb-17
108
     * Similar code in nts_cookie
109
     */
110
0
    exit(1);
111
0
  }
112
0
  buf.next += left;
113
0
  buf.left -= left;
114
115
0
  used = buf.next-xpkt->exten;
116
0
  nts_cnt.client_send++;
117
0
  return used;
118
0
}
119
120
168
bool extens_server_recv(struct ntspacket_t *ntspacket, uint8_t *pkt, int lng) {
121
168
  struct BufCtl_t buf;
122
168
  uint16_t aead;
123
168
  int noncelen, cmaclen;
124
168
  bool sawcookie, sawAEEF;
125
168
  int cookielen;      /* cookie and placeholder(s) */
126
127
168
  nts_cnt.server_recv_bad++;    /* assume bad, undo if OK */
128
129
168
  buf.next = pkt+LEN_PKT_NOMAC;
130
168
  buf.left = lng-LEN_PKT_NOMAC;
131
132
168
  sawcookie = sawAEEF = false;
133
168
  cookielen = 0;
134
168
  ntspacket->uidlen = 0;
135
168
  ntspacket->needed = 0;
136
137
883
  while (buf.left >= NTS_KE_HDR_LNG) {
138
833
    uint16_t type;
139
833
    bool critical = false;
140
833
    int length, adlength;
141
833
    size_t outlen;
142
833
    uint8_t *nonce, *cmac;
143
833
    bool ok;
144
145
833
    type = ex_next_record(&buf, &length); /* length excludes header */
146
833
    if (length&3 || length > buf.left || length < 0) {
147
53
      return false;
148
53
    }
149
780
    if (NTS_CRITICAL & type) {
150
422
      critical = true;
151
422
      type &= ~NTS_CRITICAL;
152
422
    }
153
780
    switch (type) {
154
289
        case Unique_Identifier:
155
289
      if (length > NTS_UID_MAX_LENGTH) {
156
8
        return false;
157
8
      }
158
281
      ntspacket->uidlen = length;
159
281
      next_bytes(&buf, ntspacket->UID, length);
160
281
      break;
161
21
        case NTS_Cookie:
162
      /* cookies and placeholders must be the same length
163
       * in order to avoid amplification attacks.
164
       */
165
21
      if (sawcookie) {
166
0
        return false; /* second cookie */
167
0
      }
168
21
      if (0 == cookielen) {
169
2
        cookielen = length;
170
2
      }
171
19
      else if (length != cookielen) {
172
18
        return false;
173
18
      }
174
3
      ok = nts_unpack_cookie(buf.next, length, &aead, ntspacket->c2s,
175
3
                 ntspacket->s2c, &ntspacket->keylen);
176
3
      if (!ok) {
177
3
        return false;
178
3
      }
179
0
      buf.next += length;
180
0
      buf.left -= length;
181
0
      sawcookie = true;
182
0
      ntspacket->needed++;
183
0
      ntspacket->aead = aead;
184
0
      break;
185
449
        case NTS_Cookie_Placeholder:
186
449
      if (0 == cookielen) {
187
362
        cookielen = length;
188
362
      }
189
87
      else if (length != cookielen) {
190
15
        return false;
191
15
      }
192
434
      ntspacket->needed++;
193
434
      buf.next += length;
194
434
      buf.left -= length;
195
434
      break;
196
3
        case NTS_AEEF:
197
3
      if (!sawcookie) {
198
3
        return false; /* no cookie yet, no c2s */
199
3
      }
200
0
      if (length != NTP_EX_HDR_LNG+NONCE_LENGTH+CMAC_LENGTH) {
201
0
        return false;
202
0
      }
203
      /* Additional data is up to this exten. */
204
      /* backup over header */
205
0
      adlength = buf.next-NTP_EX_HDR_LNG-pkt;
206
0
      noncelen = next_uint16(&buf);
207
0
      cmaclen = next_uint16(&buf);
208
0
      if (noncelen & 3) {
209
0
        return false; /* would require padding */
210
0
      }
211
0
      if (CMAC_LENGTH != cmaclen) {
212
0
        return false;
213
0
      }
214
0
      nonce = buf.next;
215
0
      cmac = nonce+NONCE_LENGTH;
216
0
      outlen = 6;
217
0
      ok = AES_SIV_Decrypt(wire_ctx,
218
0
               NULL, &outlen,
219
0
               ntspacket->c2s, ntspacket->keylen,
220
0
               nonce, noncelen,
221
0
               cmac, CMAC_LENGTH,
222
0
               pkt, adlength);
223
0
      if (!ok) {
224
0
        return false;
225
0
      }
226
0
      if (0 != outlen) {
227
0
        return false;
228
0
      }
229
      /* we already used 2 length slots way above*/
230
0
      length -= (NTP_EX_U16_LNG+NTP_EX_U16_LNG);
231
0
      buf.next += length;
232
0
      buf.left -= length;
233
0
      if (0 != buf.left) {
234
0
        return false; /* Reject extens after AEEF block */
235
0
      }
236
0
      sawAEEF = true;
237
0
      break;
238
18
        default:
239
      /* Non NTS extensions on requests at server.
240
       * Call out when we get some that we want.
241
       * Until then, it's probably a bug. */
242
18
      if (critical) {
243
13
        return false;
244
13
      }
245
5
      buf.next += length;
246
5
      buf.left -= length;
247
5
      return false;
248
780
    }
249
780
  }
250
251
50
  if (!sawAEEF) {
252
50
    return false;
253
50
  }
254
0
  if (buf.left > 0)
255
0
    return false;
256
257
  //  printf("ESRx: %d, %d, %d\n",
258
  //      lng-LEN_PKT_NOMAC, ntspacket->needed, ntspacket->keylen);
259
0
  ntspacket->valid = true;
260
0
  nts_cnt.server_recv_good++;
261
0
  nts_cnt.server_recv_bad--;
262
0
  return true;
263
0
}
264
265
0
int extens_server_send(struct ntspacket_t *ntspacket, struct pkt *xpkt) {
266
0
  struct BufCtl_t buf;
267
0
  int used, adlength;
268
0
  size_t left;
269
0
  uint8_t *nonce, *packet;
270
0
  uint8_t *plaintext, *ciphertext;;
271
0
  uint8_t cookie[NTS_MAX_COOKIELEN];
272
0
  int cookielen, plainleng, aeadlen;
273
0
  bool ok;
274
275
  /* get first cookie now so we have length */
276
0
  cookielen = nts_make_cookie(cookie, ntspacket->aead,
277
0
            ntspacket->c2s, ntspacket->s2c, ntspacket->keylen);
278
279
0
  packet = (uint8_t*)xpkt;
280
0
  buf.next = xpkt->exten;
281
0
  buf.left = MAX_EXT_LEN;
282
283
  /* UID */
284
0
  if (0 < ntspacket->uidlen)
285
0
    ex_append_record_bytes(&buf, Unique_Identifier,
286
0
               ntspacket->UID, ntspacket->uidlen);
287
288
0
  adlength = buf.next-packet;   /* up to here is Additional Data */
289
290
  /* length of whole AEEF */
291
0
  plainleng = ntspacket->needed*(NTP_EX_HDR_LNG+cookielen);
292
  /* length of whole AEEF header */
293
0
  aeadlen = NTP_EX_U16_LNG*2+NONCE_LENGTH+CMAC_LENGTH + plainleng;
294
0
  ex_append_header(&buf, NTS_AEEF, aeadlen);
295
0
  append_uint16(&buf, NONCE_LENGTH);
296
0
  append_uint16(&buf, plainleng+CMAC_LENGTH);
297
298
0
  nonce = buf.next;
299
0
  ntp_RAND_bytes(nonce, NONCE_LENGTH);
300
0
  buf.next += NONCE_LENGTH;
301
0
  buf.left -= NONCE_LENGTH;
302
303
0
  ciphertext = buf.next;  /* cipher text starts here */
304
0
  left = buf.left;
305
0
  buf.next += CMAC_LENGTH; /* skip space for CMAC */
306
0
  buf.left -= CMAC_LENGTH;
307
0
  plaintext = buf.next;   /* encrypt in place */
308
309
0
  ex_append_record_bytes(&buf, NTS_Cookie,
310
0
             cookie, cookielen);
311
0
  for (int i=1; i<ntspacket->needed; i++) {
312
    /* WARN: This may get too big for the MTU. See length calculation above.
313
     * Responses are the same length as requests to avoid DDoS amplification.
314
     * So if it got to us, there is a good chance it will get back.  */
315
0
    nts_make_cookie(cookie, ntspacket->aead,
316
0
        ntspacket->c2s, ntspacket->s2c, ntspacket->keylen);
317
0
    ex_append_record_bytes(&buf, NTS_Cookie,
318
0
               cookie, cookielen);
319
0
  }
320
321
  //printf("ESSa: %d, %d, %d, %d\n",
322
  //  adlength, plainleng, cookielen, ntspacket->needed);
323
324
0
  ok = AES_SIV_Encrypt(wire_ctx,
325
0
           ciphertext, &left,   /* left: in: max out length, out: length used */
326
0
           ntspacket->s2c, ntspacket->keylen,
327
0
           nonce, NONCE_LENGTH,
328
0
           plaintext, plainleng,
329
0
           packet, adlength);
330
0
  if (!ok) {
331
0
    msyslog(LOG_ERR, "NTS: extens_server_send - Error from AES_SIV_Encrypt");
332
0
    nts_log_ssl_error();
333
    /* I don't think this should happen,
334
     * so crash rather than work incorrectly.
335
     * Hal, 2019-Feb-17
336
     * Similar code in nts_cookie
337
     */
338
0
    exit(1);
339
0
  }
340
341
0
  used = buf.next-xpkt->exten;
342
343
  // printf("ESSx: %lu, %d\n", (long unsigned)left, used);
344
345
0
  nts_cnt.server_send++;
346
0
  return used;
347
0
}
348
349
0
bool extens_client_recv(struct peer *peer, uint8_t *pkt, int lng) {
350
0
  struct BufCtl_t buf;
351
0
  int idx;
352
0
  bool sawAEEF = false;
353
354
0
  nts_cnt.client_recv_bad++;  /* assume bad, undo if OK */
355
356
0
  buf.next = pkt+LEN_PKT_NOMAC;
357
0
  buf.left = lng-LEN_PKT_NOMAC;
358
359
0
  while (buf.left >= NTS_KE_HDR_LNG) {
360
0
    uint16_t type;
361
0
    bool critical = false;
362
0
    int length, adlength, noncelen;
363
0
    uint8_t *nonce, *ciphertext, *plaintext;
364
0
    size_t outlen;
365
0
    bool ok;
366
367
0
    type = ex_next_record(&buf, &length); /* length excludes header */
368
0
    if (length&3 || length > buf.left || length < 0)
369
0
      return false;
370
0
    if (NTS_CRITICAL & type) {
371
0
      critical = true;
372
0
      type &= ~NTS_CRITICAL;
373
0
    }
374
    //     printf("ECR: %d, %d, %d\n", type, length, buf.left);
375
0
    switch (type) {
376
0
        case Unique_Identifier:
377
0
      if (NTS_UID_LENGTH != length)
378
0
        return false;
379
0
      if (0 != memcmp(buf.next, peer->nts_state.UID, NTS_UID_LENGTH))
380
0
        return false;
381
0
      buf.next += length;
382
0
      buf.left -= length;
383
0
      break;
384
0
        case NTS_Cookie:
385
0
      if (!sawAEEF)
386
0
        return false;     /* reject unencrypted cookies */
387
0
      if (NTS_MAX_COOKIES <= peer->nts_state.count)
388
0
        return false;     /* reject extra cookies */
389
0
      if (length != peer->nts_state.cookielen)
390
0
        return false;     /* reject length change */
391
0
      idx = peer->nts_state.writeIdx++;
392
0
      memcpy((uint8_t*)&peer->nts_state.cookies[idx], buf.next, length);
393
0
      peer->nts_state.writeIdx = peer->nts_state.writeIdx % NTS_MAX_COOKIES;
394
0
      peer->nts_state.count++;
395
0
      buf.next += length;
396
0
      buf.left -= length;
397
0
      break;
398
0
        case NTS_AEEF:
399
0
      adlength = buf.next-NTP_EX_HDR_LNG-pkt;  /* backup over header */
400
0
      if (NTP_EX_U16_LNG*2 > length)
401
0
        return false;        /* garbage packet */
402
0
      noncelen = next_uint16(&buf);
403
0
      outlen = next_uint16(&buf);
404
0
      if (noncelen&3 || outlen&3)
405
0
        return false;        /* else round up */
406
0
      nonce = buf.next;
407
0
      ciphertext = nonce+noncelen;
408
0
      plaintext = ciphertext+CMAC_LENGTH;
409
0
      if (noncelen+CMAC_LENGTH > length)
410
0
        return false;        /* garbage packet */
411
0
      outlen = buf.left-noncelen-CMAC_LENGTH;
412
      //      printf("ECRa: %lu, %d\n", (long unsigned)outlen, noncelen);
413
0
      ok = AES_SIV_Decrypt(wire_ctx,
414
0
               plaintext, &outlen,
415
0
               peer->nts_state.s2c, peer->nts_state.keylen,
416
0
               nonce, noncelen,
417
0
               ciphertext, outlen+CMAC_LENGTH,
418
0
               pkt, adlength);
419
      //      printf("ECRb: %d, %lu\n", ok, (long unsigned)outlen);
420
0
      if (!ok)
421
0
        return false;
422
      /* setup to process encrypted headers */
423
0
      buf.next += noncelen+CMAC_LENGTH;
424
0
      buf.left -= noncelen+CMAC_LENGTH;
425
0
      sawAEEF = true;
426
0
      break;
427
0
        default:
428
      /* Non NTS extensions on reply from server.
429
       * Call out when we get some that we want.
430
       * For now, it's probably a bug. */
431
0
      if (critical)
432
0
        return false;
433
0
      buf.next += length;
434
0
      buf.left -= length;
435
0
      return false;
436
0
    }
437
0
  }
438
439
  //  printf("ECRx: %d, %d  %d, %d\n", sawAEEF, peer->nts_state.count,
440
  //      peer->nts_state.writeIdx, peer->nts_state.readIdx);
441
0
  if (!sawAEEF) {
442
0
    return false;
443
0
  }
444
0
  if (buf.left > 0)
445
0
    return false;
446
0
  nts_cnt.client_recv_good++;
447
0
  nts_cnt.client_recv_bad--;
448
0
  return true;
449
0
}
450
/* end */