]> CyberLeo.Net >> Repos - FreeBSD/releng/10.2.git/blob - contrib/ntp/libntp/authreadkeys.c
Upgrade NTP to 4.2.8p4.
[FreeBSD/releng/10.2.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 size_t nerr_loglimit = 5u;
81 static const size_t nerr_maxlimit = 15;
82
83 static void log_maybe(size_t*, const char*, ...) NTP_PRINTF(2, 3);
84
85 static void
86 log_maybe(
87         size_t     *pnerr,
88         const char *fmt  ,
89         ...)
90 {
91         va_list ap;
92         if (++(*pnerr) <= nerr_loglimit) {
93                 va_start(ap, fmt);
94                 mvsyslog(LOG_ERR, fmt, ap);
95                 va_end(ap);
96         }
97 }
98
99 /*
100  * authreadkeys - (re)read keys from a file.
101  */
102 int
103 authreadkeys(
104         const char *file
105         )
106 {
107         FILE    *fp;
108         char    *line;
109         char    *token;
110         keyid_t keyno;
111         int     keytype;
112         char    buf[512];               /* lots of room for line */
113         u_char  keystr[32];             /* Bug 2537 */
114         size_t  len;
115         size_t  j;
116         size_t  nerr;
117         /*
118          * Open file.  Complain and return if it can't be opened.
119          */
120         fp = fopen(file, "r");
121         if (fp == NULL) {
122                 msyslog(LOG_ERR, "authreadkeys: file %s: %m",
123                     file);
124                 return (0);
125         }
126         INIT_SSL();
127
128         /*
129          * Remove all existing keys
130          */
131         auth_delkeys();
132
133         /*
134          * Now read lines from the file, looking for key entries
135          */
136         nerr = 0;
137         while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
138                 if (nerr > nerr_maxlimit)
139                         break;
140                 token = nexttok(&line);
141                 if (token == NULL)
142                         continue;
143                 
144                 /*
145                  * First is key number.  See if it is okay.
146                  */
147                 keyno = atoi(token);
148                 if (keyno == 0) {
149                         log_maybe(&nerr,
150                                   "authreadkeys: cannot change key %s",
151                                   token);
152                         continue;
153                 }
154
155                 if (keyno > NTP_MAXKEY) {
156                         log_maybe(&nerr,
157                                   "authreadkeys: key %s > %d reserved for Autokey",
158                                   token, NTP_MAXKEY);
159                         continue;
160                 }
161
162                 /*
163                  * Next is keytype. See if that is all right.
164                  */
165                 token = nexttok(&line);
166                 if (token == NULL) {
167                         log_maybe(&nerr,
168                                   "authreadkeys: no key type for key %d",
169                                   keyno);
170                         continue;
171                 }
172 #ifdef OPENSSL
173                 /*
174                  * The key type is the NID used by the message digest 
175                  * algorithm. There are a number of inconsistencies in
176                  * the OpenSSL database. We attempt to discover them
177                  * here and prevent use of inconsistent data later.
178                  */
179                 keytype = keytype_from_text(token, NULL);
180                 if (keytype == 0) {
181                         log_maybe(&nerr,
182                                   "authreadkeys: invalid type for key %d",
183                                   keyno);
184                         continue;
185                 }
186                 if (EVP_get_digestbynid(keytype) == NULL) {
187                         log_maybe(&nerr,
188                                   "authreadkeys: no algorithm for key %d",
189                                   keyno);
190                         continue;
191                 }
192 #else   /* !OPENSSL follows */
193
194                 /*
195                  * The key type is unused, but is required to be 'M' or
196                  * 'm' for compatibility.
197                  */
198                 if (!(*token == 'M' || *token == 'm')) {
199                         log_maybe(&nerr,
200                                   "authreadkeys: invalid type for key %d",
201                                   keyno);
202                         continue;
203                 }
204                 keytype = KEY_TYPE_MD5;
205 #endif  /* !OPENSSL */
206
207                 /*
208                  * Finally, get key and insert it. If it is longer than 20
209                  * characters, it is a binary string encoded in hex;
210                  * otherwise, it is a text string of printable ASCII
211                  * characters.
212                  */
213                 token = nexttok(&line);
214                 if (token == NULL) {
215                         log_maybe(&nerr,
216                                   "authreadkeys: no key for key %d", keyno);
217                         continue;
218                 }
219                 len = strlen(token);
220                 if (len <= 20) {        /* Bug 2537 */
221                         MD5auth_setkey(keyno, keytype, (u_char *)token, len);
222                 } else {
223                         char    hex[] = "0123456789abcdef";
224                         u_char  temp;
225                         char    *ptr;
226                         size_t  jlim;
227
228                         jlim = min(len, 2 * sizeof(keystr));
229                         for (j = 0; j < jlim; j++) {
230                                 ptr = strchr(hex, tolower((unsigned char)token[j]));
231                                 if (ptr == NULL)
232                                         break;  /* abort decoding */
233                                 temp = (u_char)(ptr - hex);
234                                 if (j & 1)
235                                         keystr[j / 2] |= temp;
236                                 else
237                                         keystr[j / 2] = temp << 4;
238                         }
239                         if (j < jlim) {
240                                 log_maybe(&nerr,
241                                           "authreadkeys: invalid hex digit for key %d",
242                                           keyno);
243                                 continue;
244                         }
245                         MD5auth_setkey(keyno, keytype, keystr, jlim / 2);
246                 }
247         }
248         fclose(fp);
249         if (nerr > nerr_maxlimit) {
250                 msyslog(LOG_ERR,
251                         "authreadkeys: emergency break after %u errors",
252                         nerr);
253                 return (0);
254         } else if (nerr > nerr_loglimit) {
255                 msyslog(LOG_ERR,
256                         "authreadkeys: found %u more error(s)",
257                         nerr - nerr_loglimit);
258         }
259         return (1);
260 }