]> CyberLeo.Net >> Repos - FreeBSD/releng/9.3.git/blob - contrib/ntp/libntp/authreadkeys.c
o Fix invalid TCP checksums with pf(4). [EN-16:02.pf]
[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 "ntp_fp.h"
9 #include "ntp.h"
10 #include "ntp_syslog.h"
11 #include "ntp_stdlib.h"
12
13 #ifdef OPENSSL
14 #include "openssl/objects.h"
15 #include "openssl/evp.h"
16 #endif  /* OPENSSL */
17
18 /* Forwards */
19 static char *nexttok (char **);
20
21 /*
22  * nexttok - basic internal tokenizing routine
23  */
24 static char *
25 nexttok(
26         char    **str
27         )
28 {
29         register char *cp;
30         char *starttok;
31
32         cp = *str;
33
34         /*
35          * Space past white space
36          */
37         while (*cp == ' ' || *cp == '\t')
38                 cp++;
39         
40         /*
41          * Save this and space to end of token
42          */
43         starttok = cp;
44         while (*cp != '\0' && *cp != '\n' && *cp != ' '
45                && *cp != '\t' && *cp != '#')
46                 cp++;
47         
48         /*
49          * If token length is zero return an error, else set end of
50          * token to zero and return start.
51          */
52         if (starttok == cp)
53                 return NULL;
54         
55         if (*cp == ' ' || *cp == '\t')
56                 *cp++ = '\0';
57         else
58                 *cp = '\0';
59         
60         *str = cp;
61         return starttok;
62 }
63
64
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.
71  *
72  * This avoids the endless file iteration we will end up with otherwise,
73  * and also avoids overflowing the log file.
74  *
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.
78  */
79
80 static const u_int nerr_loglimit = 5u;
81 static const u_int nerr_maxlimit = 15;
82
83 static void log_maybe(u_int*, const char*, ...) NTP_PRINTF(2, 3);
84
85 typedef struct keydata KeyDataT;
86 struct keydata {
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)*/
92 };
93
94 static void
95 log_maybe(
96         u_int      *pnerr,
97         const char *fmt  ,
98         ...)
99 {
100         va_list ap;
101         if (++(*pnerr) <= nerr_loglimit) {
102                 va_start(ap, fmt);
103                 mvsyslog(LOG_ERR, fmt, ap);
104                 va_end(ap);
105         }
106 }
107
108 /*
109  * authreadkeys - (re)read keys from a file.
110  */
111 int
112 authreadkeys(
113         const char *file
114         )
115 {
116         FILE    *fp;
117         char    *line;
118         char    *token;
119         keyid_t keyno;
120         int     keytype;
121         char    buf[512];               /* lots of room for line */
122         u_char  keystr[32];             /* Bug 2537 */
123         size_t  len;
124         size_t  j;
125         u_int   nerr;
126         KeyDataT *list = NULL;
127         KeyDataT *next = NULL;
128         /*
129          * Open file.  Complain and return if it can't be opened.
130          */
131         fp = fopen(file, "r");
132         if (fp == NULL) {
133                 msyslog(LOG_ERR, "authreadkeys: file '%s': %m",
134                     file);
135                 goto onerror;
136         }
137         INIT_SSL();
138
139         /*
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.
143          */
144         nerr = 0;
145         while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
146                 if (nerr > nerr_maxlimit)
147                         break;
148                 token = nexttok(&line);
149                 if (token == NULL)
150                         continue;
151                 
152                 /*
153                  * First is key number.  See if it is okay.
154                  */
155                 keyno = atoi(token);
156                 if (keyno == 0) {
157                         log_maybe(&nerr,
158                                   "authreadkeys: cannot change key %s",
159                                   token);
160                         continue;
161                 }
162
163                 if (keyno > NTP_MAXKEY) {
164                         log_maybe(&nerr,
165                                   "authreadkeys: key %s > %d reserved for Autokey",
166                                   token, NTP_MAXKEY);
167                         continue;
168                 }
169
170                 /*
171                  * Next is keytype. See if that is all right.
172                  */
173                 token = nexttok(&line);
174                 if (token == NULL) {
175                         log_maybe(&nerr,
176                                   "authreadkeys: no key type for key %d",
177                                   keyno);
178                         continue;
179                 }
180 #ifdef OPENSSL
181                 /*
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.
186                  */
187                 keytype = keytype_from_text(token, NULL);
188                 if (keytype == 0) {
189                         log_maybe(&nerr,
190                                   "authreadkeys: invalid type for key %d",
191                                   keyno);
192                         continue;
193                 }
194                 if (EVP_get_digestbynid(keytype) == NULL) {
195                         log_maybe(&nerr,
196                                   "authreadkeys: no algorithm for key %d",
197                                   keyno);
198                         continue;
199                 }
200 #else   /* !OPENSSL follows */
201
202                 /*
203                  * The key type is unused, but is required to be 'M' or
204                  * 'm' for compatibility.
205                  */
206                 if (!(*token == 'M' || *token == 'm')) {
207                         log_maybe(&nerr,
208                                   "authreadkeys: invalid type for key %d",
209                                   keyno);
210                         continue;
211                 }
212                 keytype = KEY_TYPE_MD5;
213 #endif  /* !OPENSSL */
214
215                 /*
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
219                  * characters.
220                  */
221                 token = nexttok(&line);
222                 if (token == NULL) {
223                         log_maybe(&nerr,
224                                   "authreadkeys: no key for key %d", keyno);
225                         continue;
226                 }
227                 next = NULL;
228                 len = strlen(token);
229                 if (len <= 20) {        /* Bug 2537 */
230                         next = emalloc(sizeof(KeyDataT) + len);
231                         next->keyid   = keyno;
232                         next->keytype = keytype;
233                         next->seclen  = len;
234                         memcpy(next->secbuf, token, len);
235                 } else {
236                         static const char hex[] = "0123456789abcdef";
237                         u_char  temp;
238                         char    *ptr;
239                         size_t  jlim;
240
241                         jlim = min(len, 2 * sizeof(keystr));
242                         for (j = 0; j < jlim; j++) {
243                                 ptr = strchr(hex, tolower((unsigned char)token[j]));
244                                 if (ptr == NULL)
245                                         break;  /* abort decoding */
246                                 temp = (u_char)(ptr - hex);
247                                 if (j & 1)
248                                         keystr[j / 2] |= temp;
249                                 else
250                                         keystr[j / 2] = temp << 4;
251                         }
252                         if (j < jlim) {
253                                 log_maybe(&nerr,
254                                           "authreadkeys: invalid hex digit for key %d",
255                                           keyno);
256                                 continue;
257                         }
258                         len = jlim/2; /* hmmmm.... what about odd length?!? */
259                         next = emalloc(sizeof(KeyDataT) + len);
260                         next->keyid   = keyno;
261                         next->keytype = keytype;
262                         next->seclen  = len;
263                         memcpy(next->secbuf, keystr, len);
264                 }
265                 INSIST(NULL != next);
266                 next->next = list;
267                 list = next;
268         }
269         fclose(fp);
270         if (nerr > nerr_maxlimit) {
271                 msyslog(LOG_ERR,
272                         "authreadkeys: rejecting file '%s' after %u errors (emergency break)",
273                         file, nerr);
274                 goto onerror;
275         }
276         if (nerr > 0) {
277                 msyslog(LOG_ERR,
278                         "authreadkeys: rejecting file '%s' after %u error(s)",
279                         file, nerr);
280                 goto onerror;
281         }
282
283         /* first remove old file-based keys */
284         auth_delkeys();
285         /* insert the new key material */
286         while (NULL != (next = list)) {
287                 list = next->next;
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);
292                 free(next);
293         }
294         return (1);
295
296   onerror:
297         /* Mop up temporary storage before bailing out. */
298         while (NULL != (next = list)) {
299                 list = next->next;
300                 /* purge secrets from memory before free()ing it */
301                 memset(next, 0, sizeof(*next) + next->seclen);
302                 free(next);
303         }
304         return (0);
305 }