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 /*
153 * Open file. Complain and return if it can't be opened.
154 */
155 fp = fopen(file, "r");
156 if (fp == NULL) {
157 msyslog(LOG_ERR, "authreadkeys: file '%s': %m",
158 file);
159 goto onerror;
160 }
161 INIT_SSL();
162
163 /*
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.
167 */
168 nerr = 0;
169 while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
170 if (nerr > nerr_maxlimit)
171 break;
172 token = nexttok(&line);
173 if (token == NULL)
174 continue;
175
176 /*
177 * First is key number. See if it is okay.
178 */
179 keyno = atoi(token);
180 if (keyno < 1) {
181 log_maybe(&nerr,
182 "authreadkeys: cannot change key %s",
183 token);
184 continue;
185 }
186
187 if (keyno > NTP_MAXKEY) {
188 log_maybe(&nerr,
189 "authreadkeys: key %s > %d reserved for Autokey",
190 token, NTP_MAXKEY);
191 continue;
192 }
193
194 /*
195 * Next is keytype. See if that is all right.
196 */
197 token = nexttok(&line);
198 if (token == NULL) {
199 log_maybe(&nerr,
200 "authreadkeys: no key type for key %d",
201 keyno);
202 continue;
203 }
204
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.
211 */
212 #ifdef OPENSSL
213 /*
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.
218 */
219 keytype = keytype_from_text(token, NULL);
220 if (keytype == 0) {
221 log_maybe(NULL,
222 "authreadkeys: invalid type for key %d",
223 keyno);
224 # ifdef ENABLE_CMAC
225 } else if (NID_cmac != keytype &&
226 EVP_get_digestbynid(keytype) == NULL) {
227 log_maybe(NULL,
228 "authreadkeys: no algorithm for key %d",
229 keyno);
230 keytype = 0;
231 # endif /* ENABLE_CMAC */
232 }
233 #else /* !OPENSSL follows */
234 /*
235 * The key type is unused, but is required to be 'M' or
236 * 'm' for compatibility.
237 */
238 if (!(*token == 'M' || *token == 'm')) {
239 log_maybe(NULL,
240 "authreadkeys: invalid type for key %d",
241 keyno);
242 keytype = 0;
243 } else {
244 keytype = KEY_TYPE_MD5;
245 }
246 #endif /* !OPENSSL */
247
248 /*
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
252 * characters.
253 */
254 token = nexttok(&line);
255 if (token == NULL) {
256 log_maybe(&nerr,
257 "authreadkeys: no key for key %d", keyno);
258 continue;
259 }
260 next = NULL;
261 len = strlen(token);
262 if (len <= 20) { /* Bug 2537 */
263 next = emalloc(sizeof(KeyDataT) + len);
264 next->keyacclist = NULL;
265 next->keyid = keyno;
266 next->keytype = keytype;
267 next->seclen = len;
268 memcpy(next->secbuf, token, len);
269 } else {
270 static const char hex[] = "0123456789abcdef";
271 u_char temp;
272 char *ptr;
273 size_t jlim;
274
275 jlim = min(len, 2 * sizeof(keystr));
276 for (j = 0; j < jlim; j++) {
277 ptr = strchr(hex, tolower((unsigned char)token[j]));
278 if (ptr == NULL)
279 break; /* abort decoding */
280 temp = (u_char)(ptr - hex);
281 if (j & 1)
282 keystr[j / 2] |= temp;
283 else
284 keystr[j / 2] = temp << 4;
285 }
286 if (j < jlim) {
287 log_maybe(&nerr,
288 "authreadkeys: invalid hex digit for key %d",
289 keyno);
290 continue;
291 }
292 len = jlim/2; /* hmmmm.... what about odd length?!? */
293 next = emalloc(sizeof(KeyDataT) + len);
294 next->keyacclist = NULL;
295 next->keyid = keyno;
296 next->keytype = keytype;
297 next->seclen = len;
298 memcpy(next->secbuf, keystr, len);
299 }
300
301 token = nexttok(&line);
302 if (token != NULL) { /* A comma-separated IP access list */
303 char *tp = token;
304
305 while (tp) {
306 char *i;
307 char *snp; /* subnet text pointer */
308 unsigned int snbits;
309 sockaddr_u addr;
310
311 i = strchr(tp, (int)',');
312 if (i) {
313 *i = '\0';
314 }
315 snp = strchr(tp, (int)'/');
316 if (snp) {
317 char *sp;
318
319 *snp++ = '\0';
320 snbits = 0;
321 sp = snp;
322
323 while (*sp != '\0') {
324 if (!isdigit((unsigned char)*sp))
325 break;
326 if (snbits > 1000)
327 break; /* overflow */
328 snbits = 10 * snbits + (*sp++ - '0'); /* ascii dependent */
329 }
330 if (*sp != '\0') {
331 log_maybe(&nerr,
332 "authreadkeys: Invalid character in subnet specification for <%s/%s> in key %d",
333 sp, snp, keyno);
334 goto nextip;
335 }
336 } else {
337 snbits = UINT_MAX;
338 }
339
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))) {
345 log_maybe(NULL,
346 "authreadkeys: excessive subnet mask <%s/%s> for key %d",
347 tp, snp, keyno);
348 }
349 next->keyacclist = keyacc_new_push(
350 next->keyacclist, &addr, snbits);
351 } else {
352 log_maybe(&nerr,
353 "authreadkeys: invalid IP address <%s> for key %d",
354 tp, keyno);
355 }
356
357 nextip:
358 if (i) {
359 tp = i + 1;
360 } else {
361 tp = 0;
362 }
363 }
364 }
365
366 /* check if this has to be weeded out... */
367 if (0 == keytype) {
368 free_keydata(next);
369 next = NULL;
370 continue;
371 }
372
373 INSIST(NULL != next);
374 next->next = list;
375 list = next;
376 }
377 fclose(fp);
378 if (nerr > 0) {
379 const char * why = "";
380 if (nerr > nerr_maxlimit)
381 why = " (emergency break)";
382 msyslog(LOG_ERR,
383 "authreadkeys: rejecting file '%s' after %u error(s)%s",
384 file, nerr, why);
385 goto onerror;
386 }
387
388 /* first remove old file-based keys */
389 auth_delkeys();
390 /* insert the new key material */
391 while (NULL != (next = list)) {
392 list = next->next;
393 MD5auth_setkey(next->keyid, next->keytype,
394 next->secbuf, next->seclen, next->keyacclist);
395 next->keyacclist = NULL; /* consumed by MD5auth_setkey */
396 free_keydata(next);
397 }
398 return (1);
399
400 onerror:
401 /* Mop up temporary storage before bailing out. */
402 while (NULL != (next = list)) {
403 list = next->next;
404 free_keydata(next);
405 }
406 return (0);
407 }
408