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 } 355