2 * authreadkeys.c - routines to support the reading of the key file
10 #include "ntp_syslog.h"
11 #include "ntp_stdlib.h"
14 #include "openssl/objects.h"
15 #include "openssl/evp.h"
19 static char *nexttok (char **);
22 * nexttok - basic internal tokenizing routine
35 * Space past white space
37 while (*cp == ' ' || *cp == '\t')
41 * Save this and space to end of token
44 while (*cp != '\0' && *cp != '\n' && *cp != ' '
45 && *cp != '\t' && *cp != '#')
49 * If token length is zero return an error, else set end of
50 * token to zero and return start.
55 if (*cp == ' ' || *cp == '\t')
65 /* TALOS-CAN-0055: possibly DoS attack by setting the key file to the
66 * log file. This is hard to prevent (it would need to check two files
67 * to be the same on the inode level, which will not work so easily with
68 * Windows or VMS) but we can avoid the self-amplification loop: We only
69 * log the first 5 errors, silently ignore the next 10 errors, and give
70 * up when when we have found more than 15 errors.
72 * This avoids the endless file iteration we will end up with otherwise,
73 * and also avoids overflowing the log file.
75 * Nevertheless, once this happens, the keys are gone since this would
76 * require a save/swap strategy that is not easy to apply due to the
77 * data on global/static level.
80 static const size_t nerr_loglimit = 5u;
81 static const size_t nerr_maxlimit = 15;
83 static void log_maybe(size_t*, const char*, ...) NTP_PRINTF(2, 3);
92 if (++(*pnerr) <= nerr_loglimit) {
94 mvsyslog(LOG_ERR, fmt, ap);
100 * authreadkeys - (re)read keys from a file.
112 char buf[512]; /* lots of room for line */
113 u_char keystr[32]; /* Bug 2537 */
118 * Open file. Complain and return if it can't be opened.
120 fp = fopen(file, "r");
122 msyslog(LOG_ERR, "authreadkeys: file %s: %m",
129 * Remove all existing keys
134 * Now read lines from the file, looking for key entries
137 while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
138 if (nerr > nerr_maxlimit)
140 token = nexttok(&line);
145 * First is key number. See if it is okay.
150 "authreadkeys: cannot change key %s",
155 if (keyno > NTP_MAXKEY) {
157 "authreadkeys: key %s > %d reserved for Autokey",
163 * Next is keytype. See if that is all right.
165 token = nexttok(&line);
168 "authreadkeys: no key type for key %d",
174 * The key type is the NID used by the message digest
175 * algorithm. There are a number of inconsistencies in
176 * the OpenSSL database. We attempt to discover them
177 * here and prevent use of inconsistent data later.
179 keytype = keytype_from_text(token, NULL);
182 "authreadkeys: invalid type for key %d",
186 if (EVP_get_digestbynid(keytype) == NULL) {
188 "authreadkeys: no algorithm for key %d",
192 #else /* !OPENSSL follows */
195 * The key type is unused, but is required to be 'M' or
196 * 'm' for compatibility.
198 if (!(*token == 'M' || *token == 'm')) {
200 "authreadkeys: invalid type for key %d",
204 keytype = KEY_TYPE_MD5;
205 #endif /* !OPENSSL */
208 * Finally, get key and insert it. If it is longer than 20
209 * characters, it is a binary string encoded in hex;
210 * otherwise, it is a text string of printable ASCII
213 token = nexttok(&line);
216 "authreadkeys: no key for key %d", keyno);
220 if (len <= 20) { /* Bug 2537 */
221 MD5auth_setkey(keyno, keytype, (u_char *)token, len);
223 char hex[] = "0123456789abcdef";
228 jlim = min(len, 2 * sizeof(keystr));
229 for (j = 0; j < jlim; j++) {
230 ptr = strchr(hex, tolower((unsigned char)token[j]));
232 break; /* abort decoding */
233 temp = (u_char)(ptr - hex);
235 keystr[j / 2] |= temp;
237 keystr[j / 2] = temp << 4;
241 "authreadkeys: invalid hex digit for key %d",
245 MD5auth_setkey(keyno, keytype, keystr, jlim / 2);
249 if (nerr > nerr_maxlimit) {
251 "authreadkeys: emergency break after %u errors",
254 } else if (nerr > nerr_loglimit) {
256 "authreadkeys: found %u more error(s)",
257 nerr - nerr_loglimit);