Coverage Report

Created: 2026-08-31 07:18

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/ntp-dev/libntp/a_md5encrypt.c
Line
Count
Source
1
/*
2
 *  digest support for NTP, MD5 and with OpenSSL more
3
 */
4
#ifdef HAVE_CONFIG_H
5
#include <config.h>
6
#endif
7
8
#include "ntp_fp.h"
9
#include "ntp_string.h"
10
#include "ntp_stdlib.h"
11
#include "ntp.h"
12
#include "isc/string.h"
13
14
typedef struct {
15
  const void *  buf;
16
  size_t    len;
17
} robuffT;
18
19
typedef struct {
20
  void *    buf;
21
  size_t    len;
22
} rwbuffT;
23
24
bool  suppress_digest_errors;   /* for ntpq digest_alg_works() */
25
26
27
static inline void  digst_msyslog(int level, const char* fmt, ...) NTP_PRINTF(2, 3);
28
29
static inline void
30
digst_msyslog(
31
  int   level,
32
  const char *  fmt, 
33
  ...
34
  )
35
0
{
36
0
  va_list ap;
37
38
0
  if (!suppress_digest_errors) {
39
0
    va_start(ap, fmt);
40
0
    mvsyslog(level, fmt, ap);
41
0
    va_end(ap);
42
0
  }
43
0
}
44
45
46
#if defined(OPENSSL) && defined(ENABLE_CMAC)
47
static size_t
48
cmac_ctx_size(
49
  CMAC_CTX *  ctx
50
  )
51
0
{
52
0
  size_t mlen = 0;
53
54
0
  if (ctx) {
55
0
    EVP_CIPHER_CTX *  cctx;
56
0
    if (NULL != (cctx = CMAC_CTX_get0_cipher_ctx (ctx)))
57
0
      mlen = EVP_CIPHER_CTX_block_size(cctx);
58
0
  }
59
0
  return mlen;
60
0
}
61
#endif  /* OPENSSL && ENABLE_CMAC */
62
63
64
/*
65
 * Allocate and initialize a digest context.  As a speed optimization,
66
 * take an idea from ntpsec and cache the context to avoid malloc/free
67
 * overhead in time-critical paths.  ntpsec also caches the algorithms
68
 * with each key.
69
 * This is not thread-safe, but that is not a problem at present.
70
 */
71
static EVP_MD_CTX *
72
get_md_ctx(
73
  int   nid
74
  )
75
0
{
76
#ifndef OPENSSL
77
  static MD5_CTX  md5_ctx;
78
79
  DEBUG_INSIST(NID_md5 == nid);
80
  ntp_md5_init(&md5_ctx);
81
82
  return &md5_ctx;
83
#else
84
0
  if (!EVP_DigestInit(digest_ctx, EVP_get_digestbynid(nid))) {
85
0
    digst_msyslog(LOG_ERR, "%s init failed", OBJ_nid2sn(nid));
86
0
    return NULL;
87
0
  }
88
89
0
  return digest_ctx;
90
0
#endif  /* OPENSSL */
91
0
}
92
93
94
static size_t
95
make_mac(
96
  const rwbuffT * digest,
97
  int   ktype,
98
  const robuffT * key,
99
  const robuffT * msg
100
  )
101
0
{
102
  /*
103
   * Compute digest of key concatenated with packet. Note: the
104
   * key type and digest type have been verified when the key
105
   * was created.
106
   */
107
0
  size_t  retlen = 0;
108
109
0
#ifdef OPENSSL
110
111
0
  INIT_SSL();
112
113
  /* Check if CMAC key type specific code required */
114
0
#   ifdef ENABLE_CMAC
115
0
  if (ktype == NID_cmac) {
116
0
    CMAC_CTX *  ctx    = NULL;
117
0
    void const *  keyptr = key->buf;
118
0
    u_char    keybuf[AES_128_KEY_SIZE];
119
120
    /* adjust key size (zero padded buffer) if necessary */
121
0
    if (AES_128_KEY_SIZE > key->len) {
122
0
      memcpy(keybuf, keyptr, key->len);
123
0
      zero_mem((keybuf + key->len),
124
0
         (AES_128_KEY_SIZE - key->len));
125
0
      keyptr = keybuf;
126
0
    }
127
128
0
    if (NULL == (ctx = CMAC_CTX_new())) {
129
0
      digst_msyslog(LOG_ERR, "MAC encrypt: CMAC %s CTX new failed.", CMAC);
130
0
      goto cmac_fail;
131
0
    }
132
0
    if (!CMAC_Init(ctx, keyptr, AES_128_KEY_SIZE, EVP_aes_128_cbc(), NULL)) {
133
0
      digst_msyslog(LOG_ERR, "MAC encrypt: CMAC %s Init failed.",    CMAC);
134
0
      goto cmac_fail;
135
0
    }
136
0
    if (cmac_ctx_size(ctx) > digest->len) {
137
0
      digst_msyslog(LOG_ERR, "MAC encrypt: CMAC %s buf too small.",  CMAC);
138
0
      goto cmac_fail;
139
0
    }
140
0
    if (!CMAC_Update(ctx, msg->buf, msg->len)) {
141
0
      digst_msyslog(LOG_ERR, "MAC encrypt: CMAC %s Update failed.",  CMAC);
142
0
      goto cmac_fail;
143
0
    }
144
0
    if (!CMAC_Final(ctx, digest->buf, &retlen)) {
145
0
      digst_msyslog(LOG_ERR, "MAC encrypt: CMAC %s Final failed.",   CMAC);
146
0
      retlen = 0;
147
0
    }
148
0
    cmac_fail:
149
0
    if (ctx)
150
0
      CMAC_CTX_free(ctx);
151
0
  }
152
0
  else
153
0
#   endif /* ENABLE_CMAC */
154
0
  { /* generic MAC handling */
155
0
    EVP_MD_CTX *  ctx;
156
0
    u_int   uilen = 0;
157
158
0
    ctx = get_md_ctx(ktype);
159
0
    if (NULL == ctx) {
160
0
      goto mac_fail;
161
0
    }
162
0
    if ((size_t)EVP_MD_CTX_size(ctx) > digest->len) {
163
0
      digst_msyslog(LOG_ERR, "MAC encrypt: MAC %s buf too small.",
164
0
        OBJ_nid2sn(ktype));
165
0
      goto mac_fail;
166
0
    }
167
0
    if (!EVP_DigestUpdate(ctx, key->buf, (u_int)key->len)) {
168
0
      digst_msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update key failed.",
169
0
        OBJ_nid2sn(ktype));
170
0
      goto mac_fail;
171
0
    }
172
0
    if (!EVP_DigestUpdate(ctx, msg->buf, (u_int)msg->len)) {
173
0
      digst_msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update data failed.",
174
0
        OBJ_nid2sn(ktype));
175
0
      goto mac_fail;
176
0
    }
177
0
    if (!EVP_DigestFinal(ctx, digest->buf, &uilen)) {
178
0
      digst_msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Final failed.",
179
0
        OBJ_nid2sn(ktype));
180
0
      uilen = 0;
181
0
    }
182
0
    mac_fail:
183
0
    retlen = (size_t)uilen;
184
0
  }
185
186
#else /* !OPENSSL follows */
187
188
  if (NID_md5 == ktype) {
189
    EVP_MD_CTX *  ctx;
190
191
    ctx = get_md_ctx(ktype);
192
    if (digest->len < MD5_LENGTH) {
193
      msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small.");
194
    } else {
195
      ntp_md5_init(ctx);
196
      ntp_md5_update(ctx, key->buf, key->len);
197
      ntp_md5_update(ctx, msg->buf, msg->len);
198
      ntp_md5_final(digest->buf, ctx);
199
      retlen = MD5_LENGTH;
200
    }
201
  } else {
202
    msyslog(LOG_ERR, "MAC encrypt: invalid key type %d", ktype);
203
  }
204
205
#endif /* !OPENSSL */
206
207
0
  return retlen;
208
0
}
209
210
211
/*
212
 * MD5authencrypt - add message digest to provided packet buffer.
213
 * Provided buffer must have room for 24 bytes of key ID and MAC.
214
 * Returns 0 on failure or length of added MAC including key ID.
215
 */
216
size_t
217
MD5authencrypt(
218
  int   type,   /* hash algorithm */
219
  const u_char *  key,    /* key pointer */
220
  size_t    klen,   /* key length */
221
  u_int32 * pkt,    /* packet pointer */
222
  size_t    input_size  /* without MAC */
223
  )
224
0
{
225
0
  u_char  digest[EVP_MAX_MD_SIZE];
226
0
  rwbuffT digb = { digest, sizeof(digest) };
227
0
  robuffT keyb = { key, klen };
228
0
  robuffT msgb = { pkt, input_size };
229
0
  size_t  dlen;
230
231
0
  dlen = make_mac(&digb, type, &keyb, &msgb);
232
0
  if (0 == dlen) {
233
0
    return 0;
234
0
  }
235
  /*
236
   * If the digest is longer than the 20 octets truncate it.  NTPv4
237
   * MACs consist of a 4-octet key ID and a digest, total up to 24
238
   * octets.  See RFC 7822 7.5.1.3 and 7.5.1.4.
239
   * Use of a digest algorithm which produces more than 20 octets
240
   * provides increased difficulty to forge even when truncated.
241
   * The fleeting lifetime of an individual packet's MAC makes offline
242
   * attack difficult.  The basic NTP packet is 48 octets, so it is
243
   * not obvious that a digest of more than 20 octets is warranted.
244
   */
245
0
  if (dlen > MAX_MDG_LEN) {
246
0
    dlen = MAX_MDG_LEN;
247
0
  }
248
0
  memcpy((u_char *)pkt + input_size + KEY_MAC_LEN, digest, dlen);
249
0
  return (dlen + KEY_MAC_LEN);
250
0
}
251
252
253
/*
254
 * MD5authdecrypt - verify MD5 message authenticator
255
 *
256
 * Returns TRUE if digest valid.
257
 */
258
bool
259
MD5authdecrypt(
260
  int   type, /* hash algorithm */
261
  const u_char *  key,  /* key pointer */
262
  size_t    klen, /* key length */
263
  u_int32 * pkt,  /* packet pointer */
264
  size_t    length, /* packet length */
265
  size_t    mac_size, /* including key id */
266
  keyid_t   keyno   /* key id (for err log) */
267
  )
268
0
{
269
0
  u_char  digest[EVP_MAX_MD_SIZE];
270
0
  rwbuffT digb = { digest, sizeof(digest) };
271
0
  robuffT keyb = { key, klen };
272
0
  robuffT msgb = { pkt, length };
273
0
  size_t  dlen;
274
275
0
  dlen = make_mac(&digb, type, &keyb, &msgb);
276
277
  /* If the digest is longer than 20 octets truncate. */
278
0
  if (dlen > MAX_MDG_LEN) {
279
0
    dlen = MAX_MDG_LEN;
280
0
  }
281
0
  if (mac_size != dlen + KEY_MAC_LEN) {
282
0
    digst_msyslog(LOG_ERR,
283
0
      "MAC decrypt: MAC length error: %u not %u for key %u",
284
0
      (u_int)mac_size, (u_int)(dlen + KEY_MAC_LEN), keyno);
285
0
    return FALSE;
286
0
  }
287
0
  return !isc_tsmemcmp(digest,
288
0
     (u_char *)pkt + length + KEY_MAC_LEN, dlen);
289
0
}
290
291
/*
292
 * Calculate the reference id from the address. If it is an IPv4
293
 * address, use it as is. If it is an IPv6 address, do a md5 on
294
 * it and use the bottom 4 bytes.
295
 * The result is in network byte order for IPv4 addreseses.  For
296
 * IPv6, ntpd long differed in the hash calculated on big-endian
297
 * vs. little-endian because the first four bytes of the MD5 hash
298
 * were used as a u_int32 without any byte swapping.  This broke
299
 * the refid-based loop detection between mixed-endian systems.
300
 * In order to preserve behavior on the more-common little-endian
301
 * systems, the hash is now byte-swapped on big-endian systems to
302
 * match the little-endian hash.  This is ugly but it seems better
303
 * than changing the IPv6 refid calculation on the more-common
304
 * systems.
305
 * This is not thread safe, not a problem so far.
306
 */
307
u_int32
308
addr2refid(sockaddr_u *addr)
309
4
{
310
4
  static MD5_CTX  md5_ctx;
311
4
  union u_tag {
312
4
    u_char    digest[MD5_DIGEST_LENGTH];
313
4
    u_int32   addr_refid;
314
4
  } u;
315
316
4
  if (IS_IPV4(addr)) {
317
3
    return (NSRCADR(addr));
318
3
  }
319
  /* MD5 is not used for authentication here. */
320
1
  ntp_md5_init(&md5_ctx);
321
1
  ntp_md5_update(&md5_ctx, &SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr)));
322
1
  ntp_md5_final(u.digest, &md5_ctx);
323
#ifdef WORDS_BIGENDIAN
324
  u.addr_refid = BYTESWAP32(u.addr_refid);
325
#endif
326
1
  return u.addr_refid;
327
4
}