1 /*
2 * authreadkeys.c - routines to support the reading of the key file
3 */
4 #include <config.h>
5 #include <stdio.h>
6 #include <ctype.h>
7
8 #include "ntpd.h" /* Only for DPRINTF */
9 #include "ntp_fp.h"
10 #include "ntp.h"
11 #include "ntp_syslog.h"
12 #include "ntp_stdlib.h"
13 #include "ntp_keyacc.h"
14
15 #ifdef OPENSSL
16 #include "openssl/objects.h"
17 #include "openssl/evp.h"
18 #endif /* OPENSSL */
19
20 /* Forwards */
21 static char *nexttok (char **);
22
23 /*
24 * nexttok - basic internal tokenizing routine
25 */
26 static char *
nexttok(char ** str)27 nexttok(
28 char **str
29 )
30 {
31 register char *cp;
32 char *starttok;
33
34 cp = *str;
35
36 /*
37 * Space past white space
38 */
39 while (*cp == ' ' || *cp == '\t')
40 cp++;
41
42 /*
43 * Save this and space to end of token
44 */
45 starttok = cp;
46 while (*cp != '\0' && *cp != '\n' && *cp != ' '
47 && *cp != '\t' && *cp != '#')
48 cp++;
49
50 /*
51 * If token length is zero return an error, else set end of
52 * token to zero and return start.
53 */
54 if (starttok == cp)
55 return NULL;
56
57 if (*cp == ' ' || *cp == '\t')
58 *cp++ = '\0';
59 else
60 *cp = '\0';
61
62 *str = cp;
63 return starttok;
64 }
65
66
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.
73 *
74 * This avoids the endless file iteration we will end up with otherwise,
75 * and also avoids overflowing the log file.
76 *
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.
80 */
81
82 static const u_int nerr_loglimit = 5u;
83 static const u_int nerr_maxlimit = 15;
84
85 static void log_maybe(u_int*, const char*, ...) NTP_PRINTF(2, 3);
86
87 typedef struct keydata KeyDataT;
88 struct keydata {
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)*/
95 };
96
97 static void
log_maybe(u_int * pnerr,const char * fmt,...)98 log_maybe(
99 u_int *pnerr,
100 const char *fmt ,
101 ...)
102 {
103 va_list ap;
104 if ((NULL == pnerr) || (++(*pnerr) <= nerr_loglimit)) {
105 va_start(ap, fmt);
106 mvsyslog(LOG_ERR, fmt, ap);
107 va_end(ap);
108 }
109 }
110
111 static void
free_keydata(KeyDataT * node)112 free_keydata(
113 KeyDataT *node
114 )
115 {
116 KeyAccT *kap;
117
118 if (node) {
119 while (node->keyacclist) {
120 kap = node->keyacclist;
121 node->keyacclist = kap->next;
122 free(kap);
123 }
124
125 /* purge secrets from memory before free()ing it */
126 memset(node, 0, sizeof(*node) + node->seclen);
127 free(node);
128 }
129 }
130
131 /*
132 * authreadkeys - (re)read keys from a file.
133 */
134 int
authreadkeys(const char * file)135 authreadkeys(
136 const char *file
137 )
138 {
139 FILE *fp;
140 char *line;
141 char *token;
142 keyid_t keyno;
143 int keytype;
144 char buf[512]; /* lots of room for line */
145 u_char keystr[32]; /* Bug 2537 */
146 size_t len;
147 size_t j;
148 u_int nerr;
149 KeyDataT *list = NULL;
150 KeyDataT *next = NULL;
151 /*
152 * Open file. Complain and return if it can't be opened.
153 */
154 fp = fopen(file, "r");
155 if (fp == NULL) {
156 msyslog(LOG_ERR, "authreadkeys: file '%s': %m",
157 file);
158 goto onerror;
159 }
160 INIT_SSL();
161
162 /*
163 * Now read lines from the file, looking for key entries. Put
164 * the data into temporary store for later propagation to avoid
165 * two-pass processing.
166 */
167 nerr = 0;
168 while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
169 if (nerr > nerr_maxlimit)
170 break;
171 token = nexttok(&line);
172 if (token == NULL)
173 continue;
174
175 /*
176 * First is key number. See if it is okay.
177 */
178 keyno = atoi(token);
179 if (keyno < 1) {
180 log_maybe(&nerr,
181 "authreadkeys: cannot change key %s",
182 token);
183 continue;
184 }
185
186 if (keyno > NTP_MAXKEY) {
187 log_maybe(&nerr,
188 "authreadkeys: key %s > %d reserved for Autokey",
189 token, NTP_MAXKEY);
190 continue;
191 }
192
193 /*
194 * Next is keytype. See if that is all right.
195 */
196 token = nexttok(&line);
197 if (token == NULL) {
198 log_maybe(&nerr,
199 "authreadkeys: no key type for key %d",
200 keyno);
201 continue;
202 }
203
204 /* We want to silently ignore keys where we do not
205 * support the requested digest type. OTOH, we want to
206 * make sure the file is well-formed. That means we
207 * have to process the line completely and have to
208 * finally throw away the result... This is a bit more
209 * work, but it also results in better error detection.
210 */
211 #ifdef OPENSSL
212 /*
213 * The key type is the NID used by the message digest
214 * algorithm. There are a number of inconsistencies in
215 * the OpenSSL database. We attempt to discover them
216 * here and prevent use of inconsistent data later.
217 */
218 keytype = keytype_from_text(token, NULL);
219 if (keytype == 0) {
220 log_maybe(NULL,
221 "authreadkeys: invalid type for key %d",
222 keyno);
223 } else if (EVP_get_digestbynid(keytype) == NULL) {
224 log_maybe(NULL,
225 "authreadkeys: no algorithm for key %d",
226 keyno);
227 keytype = 0;
228 }
229 #else /* !OPENSSL follows */
230 /*
231 * The key type is unused, but is required to be 'M' or
232 * 'm' for compatibility.
233 */
234 if (!(*token == 'M' || *token == 'm')) {
235 log_maybe(NULL,
236 "authreadkeys: invalid type for key %d",
237 keyno);
238 keytype = 0;
239 } else {
240 keytype = KEY_TYPE_MD5;
241 }
242 #endif /* !OPENSSL */
243
244 /*
245 * Finally, get key and insert it. If it is longer than 20
246 * characters, it is a binary string encoded in hex;
247 * otherwise, it is a text string of printable ASCII
248 * characters.
249 */
250 token = nexttok(&line);
251 if (token == NULL) {
252 log_maybe(&nerr,
253 "authreadkeys: no key for key %d", keyno);
254 continue;
255 }
256 next = NULL;
257 len = strlen(token);
258 if (len <= 20) { /* Bug 2537 */
259 next = emalloc(sizeof(KeyDataT) + len);
260 next->keyacclist = NULL;
261 next->keyid = keyno;
262 next->keytype = keytype;
263 next->seclen = len;
264 memcpy(next->secbuf, token, len);
265 } else {
266 static const char hex[] = "0123456789abcdef";
267 u_char temp;
268 char *ptr;
269 size_t jlim;
270
271 jlim = min(len, 2 * sizeof(keystr));
272 for (j = 0; j < jlim; j++) {
273 ptr = strchr(hex, tolower((unsigned char)token[j]));
274 if (ptr == NULL)
275 break; /* abort decoding */
276 temp = (u_char)(ptr - hex);
277 if (j & 1)
278 keystr[j / 2] |= temp;
279 else
280 keystr[j / 2] = temp << 4;
281 }
282 if (j < jlim) {
283 log_maybe(&nerr,
284 "authreadkeys: invalid hex digit for key %d",
285 keyno);
286 continue;
287 }
288 len = jlim/2; /* hmmmm.... what about odd length?!? */
289 next = emalloc(sizeof(KeyDataT) + len);
290 next->keyacclist = NULL;
291 next->keyid = keyno;
292 next->keytype = keytype;
293 next->seclen = len;
294 memcpy(next->secbuf, keystr, len);
295 }
296
297 token = nexttok(&line);
298 DPRINTF(0, ("authreadkeys: full access list <%s>\n", (token) ? token : "NULL"));
299 if (token != NULL) { /* A comma-separated IP access list */
300 char *tp = token;
301
302 while (tp) {
303 char *i;
304 sockaddr_u addr;
305
306 i = strchr(tp, (int)',');
307 if (i)
308 *i = '\0';
309 DPRINTF(0, ("authreadkeys: access list: <%s>\n", tp));
310
311 if (is_ip_address(tp, AF_UNSPEC, &addr)) {
312 next->keyacclist = keyacc_new_push(
313 next->keyacclist, &addr);
314 } else {
315 log_maybe(&nerr,
316 "authreadkeys: invalid IP address <%s> for key %d",
317 tp, keyno);
318 }
319
320 if (i) {
321 tp = i + 1;
322 } else {
323 tp = 0;
324 }
325 }
326 }
327
328 /* check if this has to be weeded out... */
329 if (0 == keytype) {
330 free_keydata(next);
331 next = NULL;
332 continue;
333 }
334
335 INSIST(NULL != next);
336 next->next = list;
337 list = next;
338 }
339 fclose(fp);
340 if (nerr > 0) {
341 const char * why = "";
342 if (nerr > nerr_maxlimit)
343 why = " (emergency break)";
344 msyslog(LOG_ERR,
345 "authreadkeys: rejecting file '%s' after %u error(s)%s",
346 file, nerr, why);
347 goto onerror;
348 }
349
350 /* first remove old file-based keys */
351 auth_delkeys();
352 /* insert the new key material */
353 while (NULL != (next = list)) {
354 list = next->next;
355 MD5auth_setkey(next->keyid, next->keytype,
356 next->secbuf, next->seclen, next->keyacclist);
357 next->keyacclist = NULL; /* consumed by MD5auth_setkey */
358 free_keydata(next);
359 }
360 return (1);
361
362 onerror:
363 /* Mop up temporary storage before bailing out. */
364 while (NULL != (next = list)) {
365 list = next->next;
366 free_keydata(next);
367 }
368 return (0);
369 }
370