Coverage Report

Created: 2026-02-26 06:20

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
#if defined(OPENSSL) && defined(ENABLE_CMAC)
25
static size_t
26
cmac_ctx_size(
27
  CMAC_CTX *  ctx
28
  )
29
0
{
30
0
  size_t mlen = 0;
31
32
0
  if (ctx) {
33
0
    EVP_CIPHER_CTX *  cctx;
34
0
    if (NULL != (cctx = CMAC_CTX_get0_cipher_ctx (ctx)))
35
0
      mlen = EVP_CIPHER_CTX_block_size(cctx);
36
0
  }
37
0
  return mlen;
38
0
}
39
#endif  /* OPENSSL && ENABLE_CMAC */
40
41
42
/*
43
 * Allocate and initialize a digest context.  As a speed optimization,
44
 * take an idea from ntpsec and cache the context to avoid malloc/free
45
 * overhead in time-critical paths.  ntpsec also caches the algorithms
46
 * with each key.
47
 * This is not thread-safe, but that is
48
 * not a problem at present.
49
 */
50
static EVP_MD_CTX *
51
get_md_ctx(
52
  int   nid
53
  )
54
0
{
55
#ifndef OPENSSL
56
  static MD5_CTX  md5_ctx;
57
58
  DEBUG_INSIST(NID_md5 == nid);
59
  MD5Init(&md5_ctx);
60
61
  return &md5_ctx;
62
#else
63
0
  if (!EVP_DigestInit(digest_ctx, EVP_get_digestbynid(nid))) {
64
0
    msyslog(LOG_ERR, "%s init failed", OBJ_nid2sn(nid));
65
0
    return NULL;
66
0
  }
67
68
0
  return digest_ctx;
69
0
#endif  /* OPENSSL */
70
0
}
71
72
73
static size_t
74
make_mac(
75
  const rwbuffT * digest,
76
  int   ktype,
77
  const robuffT * key,
78
  const robuffT * msg
79
  )
80
0
{
81
  /*
82
   * Compute digest of key concatenated with packet. Note: the
83
   * key type and digest type have been verified when the key
84
   * was created.
85
   */
86
0
  size_t  retlen = 0;
87
88
0
#ifdef OPENSSL
89
90
0
  INIT_SSL();
91
92
  /* Check if CMAC key type specific code required */
93
0
#   ifdef ENABLE_CMAC
94
0
  if (ktype == NID_cmac) {
95
0
    CMAC_CTX *  ctx    = NULL;
96
0
    void const *  keyptr = key->buf;
97
0
    u_char    keybuf[AES_128_KEY_SIZE];
98
99
    /* adjust key size (zero padded buffer) if necessary */
100
0
    if (AES_128_KEY_SIZE > key->len) {
101
0
      memcpy(keybuf, keyptr, key->len);
102
0
      zero_mem((keybuf + key->len),
103
0
         (AES_128_KEY_SIZE - key->len));
104
0
      keyptr = keybuf;
105
0
    }
106
107
0
    if (NULL == (ctx = CMAC_CTX_new())) {
108
0
      msyslog(LOG_ERR, "MAC encrypt: CMAC %s CTX new failed.", CMAC);
109
0
      goto cmac_fail;
110
0
    }
111
0
    if (!CMAC_Init(ctx, keyptr, AES_128_KEY_SIZE, EVP_aes_128_cbc(), NULL)) {
112
0
      msyslog(LOG_ERR, "MAC encrypt: CMAC %s Init failed.",    CMAC);
113
0
      goto cmac_fail;
114
0
    }
115
0
    if (cmac_ctx_size(ctx) > digest->len) {
116
0
      msyslog(LOG_ERR, "MAC encrypt: CMAC %s buf too small.",  CMAC);
117
0
      goto cmac_fail;
118
0
    }
119
0
    if (!CMAC_Update(ctx, msg->buf, msg->len)) {
120
0
      msyslog(LOG_ERR, "MAC encrypt: CMAC %s Update failed.",  CMAC);
121
0
      goto cmac_fail;
122
0
    }
123
0
    if (!CMAC_Final(ctx, digest->buf, &retlen)) {
124
0
      msyslog(LOG_ERR, "MAC encrypt: CMAC %s Final failed.",   CMAC);
125
0
      retlen = 0;
126
0
    }
127
0
    cmac_fail:
128
0
    if (ctx)
129
0
      CMAC_CTX_free(ctx);
130
0
  }
131
0
  else
132
0
#   endif /* ENABLE_CMAC */
133
0
  { /* generic MAC handling */
134
0
    EVP_MD_CTX *  ctx;
135
0
    u_int   uilen = 0;
136
137
0
    ctx = get_md_ctx(ktype);
138
0
    if (NULL == ctx) {
139
0
      goto mac_fail;
140
0
    }
141
0
    if ((size_t)EVP_MD_CTX_size(ctx) > digest->len) {
142
0
      msyslog(LOG_ERR, "MAC encrypt: MAC %s buf too small.",
143
0
        OBJ_nid2sn(ktype));
144
0
      goto mac_fail;
145
0
    }
146
0
    if (!EVP_DigestUpdate(ctx, key->buf, (u_int)key->len)) {
147
0
      msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update key failed.",
148
0
        OBJ_nid2sn(ktype));
149
0
      goto mac_fail;
150
0
    }
151
0
    if (!EVP_DigestUpdate(ctx, msg->buf, (u_int)msg->len)) {
152
0
      msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Update data failed.",
153
0
        OBJ_nid2sn(ktype));
154
0
      goto mac_fail;
155
0
    }
156
0
    if (!EVP_DigestFinal(ctx, digest->buf, &uilen)) {
157
0
      msyslog(LOG_ERR, "MAC encrypt: MAC %s Digest Final failed.",
158
0
        OBJ_nid2sn(ktype));
159
0
      uilen = 0;
160
0
    }
161
0
    mac_fail:
162
0
    retlen = (size_t)uilen;
163
0
  }
164
165
#else /* !OPENSSL follows */
166
167
  if (NID_md5 == ktype) {
168
    EVP_MD_CTX *  ctx;
169
170
    ctx = get_md_ctx(ktype);
171
    if (digest->len < MD5_LENGTH) {
172
      msyslog(LOG_ERR, "%s", "MAC encrypt: MAC md5 buf too small.");
173
    } else {
174
      MD5Init(ctx);
175
      MD5Update(ctx, (const void *)key->buf, key->len);
176
      MD5Update(ctx, (const void *)msg->buf, msg->len);
177
      MD5Final(digest->buf, ctx);
178
      retlen = MD5_LENGTH;
179
    }
180
  } else {
181
    msyslog(LOG_ERR, "MAC encrypt: invalid key type %d", ktype);
182
  }
183
184
#endif /* !OPENSSL */
185
186
0
  return retlen;
187
0
}
188
189
190
/*
191
 * MD5authencrypt - generate message digest
192
 *
193
 * Returns 0 on failure or length of MAC including key ID.
194
 */
195
size_t
196
MD5authencrypt(
197
  int   type, /* hash algorithm */
198
  const u_char *  key,  /* key pointer */
199
  size_t    klen, /* key length */
200
  u_int32 * pkt,  /* packet pointer */
201
  size_t    length  /* packet length */
202
  )
203
0
{
204
0
  u_char  digest[EVP_MAX_MD_SIZE];
205
0
  rwbuffT digb = { digest, sizeof(digest) };
206
0
  robuffT keyb = { key, klen };
207
0
  robuffT msgb = { pkt, length };
208
0
  size_t  dlen;
209
210
0
  dlen = make_mac(&digb, type, &keyb, &msgb);
211
0
  if (0 == dlen) {
212
0
    return 0;
213
0
  }
214
0
  memcpy((u_char *)pkt + length + KEY_MAC_LEN, digest,
215
0
         min(dlen, MAX_MDG_LEN));
216
0
  return (dlen + KEY_MAC_LEN);
217
0
}
218
219
220
/*
221
 * MD5authdecrypt - verify MD5 message authenticator
222
 *
223
 * Returns one if digest valid, zero if invalid.
224
 */
225
int
226
MD5authdecrypt(
227
  int   type, /* hash algorithm */
228
  const u_char *  key,  /* key pointer */
229
  size_t    klen, /* key length */
230
  u_int32 * pkt,  /* packet pointer */
231
  size_t    length, /* packet length */
232
  size_t    size, /* MAC size */
233
  keyid_t   keyno   /* key id (for err log) */
234
  )
235
0
{
236
0
  u_char  digest[EVP_MAX_MD_SIZE];
237
0
  rwbuffT digb = { digest, sizeof(digest) };
238
0
  robuffT keyb = { key, klen };
239
0
  robuffT msgb = { pkt, length };
240
0
  size_t  dlen = 0;
241
242
0
  dlen = make_mac(&digb, type, &keyb, &msgb);
243
0
  if (0 == dlen || size != dlen + KEY_MAC_LEN) {
244
0
    msyslog(LOG_ERR,
245
0
      "MAC decrypt: MAC length error: %u not %u for key %u",
246
0
      (u_int)size, (u_int)(dlen + KEY_MAC_LEN), keyno);
247
0
    return FALSE;
248
0
  }
249
0
  return !isc_tsmemcmp(digest,
250
0
     (u_char *)pkt + length + KEY_MAC_LEN, dlen);
251
0
}
252
253
/*
254
 * Calculate the reference id from the address. If it is an IPv4
255
 * address, use it as is. If it is an IPv6 address, do a md5 on
256
 * it and use the bottom 4 bytes.
257
 * The result is in network byte order for IPv4 addreseses.  For
258
 * IPv6, ntpd long differed in the hash calculated on big-endian
259
 * vs. little-endian because the first four bytes of the MD5 hash
260
 * were used as a u_int32 without any byte swapping.  This broke
261
 * the refid-based loop detection between mixed-endian systems.
262
 * In order to preserve behavior on the more-common little-endian
263
 * systems, the hash is now byte-swapped on big-endian systems to
264
 * match the little-endian hash.  This is ugly but it seems better
265
 * than changing the IPv6 refid calculation on the more-common
266
 * systems.
267
 * This is not thread safe, not a problem so far.
268
 */
269
u_int32
270
addr2refid(sockaddr_u *addr)
271
4
{
272
4
  static MD5_CTX  md5_ctx;
273
4
  union u_tag {
274
4
    u_char    digest[MD5_DIGEST_LENGTH];
275
4
    u_int32   addr_refid;
276
4
  } u;
277
278
4
  if (IS_IPV4(addr)) {
279
3
    return (NSRCADR(addr));
280
3
  }
281
  /* MD5 is not used for authentication here. */
282
1
  MD5Init(&md5_ctx);
283
1
  MD5Update(&md5_ctx, (void *)&SOCK_ADDR6(addr), sizeof(SOCK_ADDR6(addr)));
284
1
  MD5Final(u.digest, &md5_ctx);
285
#ifdef WORDS_BIGENDIAN
286
  u.addr_refid = BYTESWAP32(u.addr_refid);
287
#endif
288
1
  return u.addr_refid;
289
4
}