2 * authreadkeys.c - routines to support the reading of the key file
8 //#include "ntpd.h" /* Only for DPRINTF */
11 #include "ntp_syslog.h"
12 #include "ntp_stdlib.h"
13 #include "ntp_keyacc.h"
16 #include "openssl/objects.h"
17 #include "openssl/evp.h"
21 static char *nexttok (char **);
24 * nexttok - basic internal tokenizing routine
37 * Space past white space
39 while (*cp == ' ' || *cp == '\t')
43 * Save this and space to end of token
46 while (*cp != '\0' && *cp != '\n' && *cp != ' '
47 && *cp != '\t' && *cp != '#')
51 * If token length is zero return an error, else set end of
52 * token to zero and return start.
57 if (*cp == ' ' || *cp == '\t')
67 /* TALOS-CAN-0055: possibly DoS attack by setting the key file to the
68 * log file. This is hard to prevent (it would need to check two files
69 * to be the same on the inode level, which will not work so easily with
70 * Windows or VMS) but we can avoid the self-amplification loop: We only
71 * log the first 5 errors, silently ignore the next 10 errors, and give
72 * up when when we have found more than 15 errors.
74 * This avoids the endless file iteration we will end up with otherwise,
75 * and also avoids overflowing the log file.
77 * Nevertheless, once this happens, the keys are gone since this would
78 * require a save/swap strategy that is not easy to apply due to the
79 * data on global/static level.
82 static const u_int nerr_loglimit = 5u;
83 static const u_int nerr_maxlimit = 15;
85 static void log_maybe(u_int*, const char*, ...) NTP_PRINTF(2, 3);
87 typedef struct keydata KeyDataT;
89 KeyDataT *next; /* queue/stack link */
90 KeyAccT *keyacclist; /* key access list */
91 keyid_t keyid; /* stored key ID */
92 u_short keytype; /* stored key type */
93 u_short seclen; /* length of secret */
94 u_char secbuf[1]; /* begin of secret (formal only)*/
104 if ((NULL == pnerr) || (++(*pnerr) <= nerr_loglimit)) {
106 mvsyslog(LOG_ERR, fmt, ap);
119 while (node->keyacclist) {
120 kap = node->keyacclist;
121 node->keyacclist = kap->next;
125 /* purge secrets from memory before free()ing it */
126 memset(node, 0, sizeof(*node) + node->seclen);
132 * authreadkeys - (re)read keys from a file.
144 char buf[512]; /* lots of room for line */
145 u_char keystr[32]; /* Bug 2537 */
149 KeyDataT *list = NULL;
150 KeyDataT *next = NULL;
153 * Open file. Complain and return if it can't be opened.
155 fp = fopen(file, "r");
157 msyslog(LOG_ERR, "authreadkeys: file '%s': %m",
164 * Now read lines from the file, looking for key entries. Put
165 * the data into temporary store for later propagation to avoid
166 * two-pass processing.
169 while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
170 if (nerr > nerr_maxlimit)
172 token = nexttok(&line);
177 * First is key number. See if it is okay.
182 "authreadkeys: cannot change key %s",
187 if (keyno > NTP_MAXKEY) {
189 "authreadkeys: key %s > %d reserved for Autokey",
195 * Next is keytype. See if that is all right.
197 token = nexttok(&line);
200 "authreadkeys: no key type for key %d",
205 /* We want to silently ignore keys where we do not
206 * support the requested digest type. OTOH, we want to
207 * make sure the file is well-formed. That means we
208 * have to process the line completely and have to
209 * finally throw away the result... This is a bit more
210 * work, but it also results in better error detection.
214 * The key type is the NID used by the message digest
215 * algorithm. There are a number of inconsistencies in
216 * the OpenSSL database. We attempt to discover them
217 * here and prevent use of inconsistent data later.
219 keytype = keytype_from_text(token, NULL);
222 "authreadkeys: invalid type for key %d",
225 } else if (NID_cmac != keytype &&
226 EVP_get_digestbynid(keytype) == NULL) {
228 "authreadkeys: no algorithm for key %d",
231 # endif /* ENABLE_CMAC */
233 #else /* !OPENSSL follows */
235 * The key type is unused, but is required to be 'M' or
236 * 'm' for compatibility.
238 if (!(*token == 'M' || *token == 'm')) {
240 "authreadkeys: invalid type for key %d",
244 keytype = KEY_TYPE_MD5;
246 #endif /* !OPENSSL */
249 * Finally, get key and insert it. If it is longer than 20
250 * characters, it is a binary string encoded in hex;
251 * otherwise, it is a text string of printable ASCII
254 token = nexttok(&line);
257 "authreadkeys: no key for key %d", keyno);
262 if (len <= 20) { /* Bug 2537 */
263 next = emalloc(sizeof(KeyDataT) + len);
264 next->keyacclist = NULL;
266 next->keytype = keytype;
268 memcpy(next->secbuf, token, len);
270 static const char hex[] = "0123456789abcdef";
275 jlim = min(len, 2 * sizeof(keystr));
276 for (j = 0; j < jlim; j++) {
277 ptr = strchr(hex, tolower((unsigned char)token[j]));
279 break; /* abort decoding */
280 temp = (u_char)(ptr - hex);
282 keystr[j / 2] |= temp;
284 keystr[j / 2] = temp << 4;
288 "authreadkeys: invalid hex digit for key %d",
292 len = jlim/2; /* hmmmm.... what about odd length?!? */
293 next = emalloc(sizeof(KeyDataT) + len);
294 next->keyacclist = NULL;
296 next->keytype = keytype;
298 memcpy(next->secbuf, keystr, len);
301 token = nexttok(&line);
302 if (token != NULL) { /* A comma-separated IP access list */
307 char *snp; /* subnet text pointer */
311 i = strchr(tp, (int)',');
315 snp = strchr(tp, (int)'/');
323 while (*sp != '\0') {
324 if (!isdigit((unsigned char)*sp))
327 break; /* overflow */
328 snbits = 10 * snbits + (*sp++ - '0'); /* ascii dependent */
332 "authreadkeys: Invalid character in subnet specification for <%s/%s> in key %d",
340 if (is_ip_address(tp, AF_UNSPEC, &addr)) {
341 /* Make sure that snbits is valid for addr */
342 if ((snbits < UINT_MAX) &&
343 ( (IS_IPV4(&addr) && snbits > 32) ||
344 (IS_IPV6(&addr) && snbits > 128))) {
346 "authreadkeys: excessive subnet mask <%s/%s> for key %d",
349 next->keyacclist = keyacc_new_push(
350 next->keyacclist, &addr, snbits);
353 "authreadkeys: invalid IP address <%s> for key %d",
366 /* check if this has to be weeded out... */
373 INSIST(NULL != next);
379 const char * why = "";
380 if (nerr > nerr_maxlimit)
381 why = " (emergency break)";
383 "authreadkeys: rejecting file '%s' after %u error(s)%s",
388 /* first remove old file-based keys */
390 /* insert the new key material */
391 while (NULL != (next = list)) {
393 MD5auth_setkey(next->keyid, next->keytype,
394 next->secbuf, next->seclen, next->keyacclist);
395 next->keyacclist = NULL; /* consumed by MD5auth_setkey */
401 /* Mop up temporary storage before bailing out. */
402 while (NULL != (next = list)) {