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 u_int nerr_loglimit = 5u;
81 static const u_int nerr_maxlimit = 15;
83 static void log_maybe(u_int*, const char*, ...) NTP_PRINTF(2, 3);
85 typedef struct keydata KeyDataT;
87 KeyDataT *next; /* queue/stack link */
88 keyid_t keyid; /* stored key ID */
89 u_short keytype; /* stored key type */
90 u_short seclen; /* length of secret */
91 u_char secbuf[1]; /* begin of secret (formal only)*/
101 if (++(*pnerr) <= nerr_loglimit) {
103 mvsyslog(LOG_ERR, fmt, ap);
109 * authreadkeys - (re)read keys from a file.
121 char buf[512]; /* lots of room for line */
122 u_char keystr[32]; /* Bug 2537 */
126 KeyDataT *list = NULL;
127 KeyDataT *next = NULL;
129 * Open file. Complain and return if it can't be opened.
131 fp = fopen(file, "r");
133 msyslog(LOG_ERR, "authreadkeys: file '%s': %m",
140 * Now read lines from the file, looking for key entries. Put
141 * the data into temporary store for later propagation to avoid
142 * two-pass processing.
145 while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
146 if (nerr > nerr_maxlimit)
148 token = nexttok(&line);
153 * First is key number. See if it is okay.
158 "authreadkeys: cannot change key %s",
163 if (keyno > NTP_MAXKEY) {
165 "authreadkeys: key %s > %d reserved for Autokey",
171 * Next is keytype. See if that is all right.
173 token = nexttok(&line);
176 "authreadkeys: no key type for key %d",
182 * The key type is the NID used by the message digest
183 * algorithm. There are a number of inconsistencies in
184 * the OpenSSL database. We attempt to discover them
185 * here and prevent use of inconsistent data later.
187 keytype = keytype_from_text(token, NULL);
190 "authreadkeys: invalid type for key %d",
194 if (EVP_get_digestbynid(keytype) == NULL) {
196 "authreadkeys: no algorithm for key %d",
200 #else /* !OPENSSL follows */
203 * The key type is unused, but is required to be 'M' or
204 * 'm' for compatibility.
206 if (!(*token == 'M' || *token == 'm')) {
208 "authreadkeys: invalid type for key %d",
212 keytype = KEY_TYPE_MD5;
213 #endif /* !OPENSSL */
216 * Finally, get key and insert it. If it is longer than 20
217 * characters, it is a binary string encoded in hex;
218 * otherwise, it is a text string of printable ASCII
221 token = nexttok(&line);
224 "authreadkeys: no key for key %d", keyno);
229 if (len <= 20) { /* Bug 2537 */
230 next = emalloc(sizeof(KeyDataT) + len);
232 next->keytype = keytype;
234 memcpy(next->secbuf, token, len);
236 static const char hex[] = "0123456789abcdef";
241 jlim = min(len, 2 * sizeof(keystr));
242 for (j = 0; j < jlim; j++) {
243 ptr = strchr(hex, tolower((unsigned char)token[j]));
245 break; /* abort decoding */
246 temp = (u_char)(ptr - hex);
248 keystr[j / 2] |= temp;
250 keystr[j / 2] = temp << 4;
254 "authreadkeys: invalid hex digit for key %d",
258 len = jlim/2; /* hmmmm.... what about odd length?!? */
259 next = emalloc(sizeof(KeyDataT) + len);
261 next->keytype = keytype;
263 memcpy(next->secbuf, keystr, len);
265 INSIST(NULL != next);
270 if (nerr > nerr_maxlimit) {
272 "authreadkeys: rejecting file '%s' after %u errors (emergency break)",
278 "authreadkeys: rejecting file '%s' after %u error(s)",
283 /* first remove old file-based keys */
285 /* insert the new key material */
286 while (NULL != (next = list)) {
288 MD5auth_setkey(next->keyid, next->keytype,
289 next->secbuf, next->seclen);
290 /* purge secrets from memory before free()ing it */
291 memset(next, 0, sizeof(*next) + next->seclen);
297 /* Mop up temporary storage before bailing out. */
298 while (NULL != (next = list)) {
300 /* purge secrets from memory before free()ing it */
301 memset(next, 0, sizeof(*next) + next->seclen);