]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/ntp/libntp/authreadkeys.c
Fix BIND remote denial of service vulnerability. [SA-16:08]
[FreeBSD/releng/9.3.git] / contrib / ntp / libntp / authreadkeys.c
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 *
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
98 log_maybe(
99         u_int      *pnerr,
100         const char *fmt  ,
101         ...)
102 {
103         va_list ap;
104         if (++(*pnerr) <= nerr_loglimit) {
105                 va_start(ap, fmt);
106                 mvsyslog(LOG_ERR, fmt, ap);
107                 va_end(ap);
108         }
109 }
110
111 /*
112  * authreadkeys - (re)read keys from a file.
113  */
114 int
115 authreadkeys(
116         const char *file
117         )
118 {
119         FILE    *fp;
120         char    *line;
121         char    *token;
122         keyid_t keyno;
123         int     keytype;
124         char    buf[512];               /* lots of room for line */
125         u_char  keystr[32];             /* Bug 2537 */
126         size_t  len;
127         size_t  j;
128         u_int   nerr;
129         KeyDataT *list = NULL;
130         KeyDataT *next = NULL;
131         /*
132          * Open file.  Complain and return if it can't be opened.
133          */
134         fp = fopen(file, "r");
135         if (fp == NULL) {
136                 msyslog(LOG_ERR, "authreadkeys: file '%s': %m",
137                     file);
138                 goto onerror;
139         }
140         INIT_SSL();
141
142         /*
143          * Now read lines from the file, looking for key entries. Put
144          * the data into temporary store for later propagation to avoid
145          * two-pass processing.
146          */
147         nerr = 0;
148         while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
149                 if (nerr > nerr_maxlimit)
150                         break;
151                 token = nexttok(&line);
152                 if (token == NULL)
153                         continue;
154                 
155                 /*
156                  * First is key number.  See if it is okay.
157                  */
158                 keyno = atoi(token);
159                 if (keyno == 0) {
160                         log_maybe(&nerr,
161                                   "authreadkeys: cannot change key %s",
162                                   token);
163                         continue;
164                 }
165
166                 if (keyno > NTP_MAXKEY) {
167                         log_maybe(&nerr,
168                                   "authreadkeys: key %s > %d reserved for Autokey",
169                                   token, NTP_MAXKEY);
170                         continue;
171                 }
172
173                 /*
174                  * Next is keytype. See if that is all right.
175                  */
176                 token = nexttok(&line);
177                 if (token == NULL) {
178                         log_maybe(&nerr,
179                                   "authreadkeys: no key type for key %d",
180                                   keyno);
181                         continue;
182                 }
183 #ifdef OPENSSL
184                 /*
185                  * The key type is the NID used by the message digest 
186                  * algorithm. There are a number of inconsistencies in
187                  * the OpenSSL database. We attempt to discover them
188                  * here and prevent use of inconsistent data later.
189                  */
190                 keytype = keytype_from_text(token, NULL);
191                 if (keytype == 0) {
192                         log_maybe(&nerr,
193                                   "authreadkeys: invalid type for key %d",
194                                   keyno);
195                         continue;
196                 }
197                 if (EVP_get_digestbynid(keytype) == NULL) {
198                         log_maybe(&nerr,
199                                   "authreadkeys: no algorithm for key %d",
200                                   keyno);
201                         continue;
202                 }
203 #else   /* !OPENSSL follows */
204
205                 /*
206                  * The key type is unused, but is required to be 'M' or
207                  * 'm' for compatibility.
208                  */
209                 if (!(*token == 'M' || *token == 'm')) {
210                         log_maybe(&nerr,
211                                   "authreadkeys: invalid type for key %d",
212                                   keyno);
213                         continue;
214                 }
215                 keytype = KEY_TYPE_MD5;
216 #endif  /* !OPENSSL */
217
218                 /*
219                  * Finally, get key and insert it. If it is longer than 20
220                  * characters, it is a binary string encoded in hex;
221                  * otherwise, it is a text string of printable ASCII
222                  * characters.
223                  */
224                 token = nexttok(&line);
225                 if (token == NULL) {
226                         log_maybe(&nerr,
227                                   "authreadkeys: no key for key %d", keyno);
228                         continue;
229                 }
230                 next = NULL;
231                 len = strlen(token);
232                 if (len <= 20) {        /* Bug 2537 */
233                         next = emalloc(sizeof(KeyDataT) + len);
234                         next->keyacclist = NULL;
235                         next->keyid   = keyno;
236                         next->keytype = keytype;
237                         next->seclen  = len;
238                         memcpy(next->secbuf, token, len);
239                 } else {
240                         static const char hex[] = "0123456789abcdef";
241                         u_char  temp;
242                         char    *ptr;
243                         size_t  jlim;
244
245                         jlim = min(len, 2 * sizeof(keystr));
246                         for (j = 0; j < jlim; j++) {
247                                 ptr = strchr(hex, tolower((unsigned char)token[j]));
248                                 if (ptr == NULL)
249                                         break;  /* abort decoding */
250                                 temp = (u_char)(ptr - hex);
251                                 if (j & 1)
252                                         keystr[j / 2] |= temp;
253                                 else
254                                         keystr[j / 2] = temp << 4;
255                         }
256                         if (j < jlim) {
257                                 log_maybe(&nerr,
258                                           "authreadkeys: invalid hex digit for key %d",
259                                           keyno);
260                                 continue;
261                         }
262                         len = jlim/2; /* hmmmm.... what about odd length?!? */
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, keystr, len);
269                 }
270
271                 token = nexttok(&line);
272 DPRINTF(0, ("authreadkeys: full access list <%s>\n", (token) ? token : "NULL"));
273                 if (token != NULL) {    /* A comma-separated IP access list */
274                         char *tp = token;
275
276                         while (tp) {
277                                 char *i;
278                                 KeyAccT ka;
279
280                                 i = strchr(tp, (int)',');
281                                 if (i)
282                                         *i = '\0';
283 DPRINTF(0, ("authreadkeys: access list:  <%s>\n", tp));
284
285                                 if (is_ip_address(tp, AF_UNSPEC, &ka.addr)) {
286                                         KeyAccT *kap;
287
288                                         kap = emalloc(sizeof(KeyAccT));
289                                         memcpy(kap, &ka, sizeof ka);
290                                         kap->next = next->keyacclist;
291                                         next->keyacclist = kap;
292                                 } else {
293                                         log_maybe(&nerr,
294                                                   "authreadkeys: invalid IP address <%s> for key %d",
295                                                   tp, keyno);
296                                 }
297
298                                 if (i) {
299                                         tp = i + 1;
300                                 } else {
301                                         tp = 0;
302                                 }
303                         }
304                 }
305
306                 INSIST(NULL != next);
307                 next->next = list;
308                 list = next;
309         }
310         fclose(fp);
311         if (nerr > nerr_maxlimit) {
312                 msyslog(LOG_ERR,
313                         "authreadkeys: rejecting file '%s' after %u errors (emergency break)",
314                         file, nerr);
315                 goto onerror;
316         }
317         if (nerr > 0) {
318                 msyslog(LOG_ERR,
319                         "authreadkeys: rejecting file '%s' after %u error(s)",
320                         file, nerr);
321                 goto onerror;
322         }
323
324         /* first remove old file-based keys */
325         auth_delkeys();
326         /* insert the new key material */
327         while (NULL != (next = list)) {
328                 list = next->next;
329                 MD5auth_setkey(next->keyid, next->keytype,
330                                next->secbuf, next->seclen, next->keyacclist);
331                 /* purge secrets from memory before free()ing it */
332                 memset(next, 0, sizeof(*next) + next->seclen);
333                 free(next);
334         }
335         return (1);
336
337   onerror:
338         /* Mop up temporary storage before bailing out. */
339         while (NULL != (next = list)) {
340                 list = next->next;
341
342                 while (next->keyacclist) {
343                         KeyAccT *kap = next->keyacclist;
344
345                         next->keyacclist = kap->next;
346                         free(kap);
347                 }
348
349                 /* purge secrets from memory before free()ing it */
350                 memset(next, 0, sizeof(*next) + next->seclen);
351                 free(next);
352         }
353         return (0);
354 }