1 /* $NetBSD: authreadkeys.c,v 1.11 2020/05/25 20:47:24 christos Exp $ */ 2 3 /* 4 * authreadkeys.c - routines to support the reading of the key file 5 */ 6 #include <config.h> 7 #include <stdio.h> 8 #include <ctype.h> 9 10 //#include "ntpd.h" /* Only for DPRINTF */ 11 //#include "ntp_fp.h" 12 #include "ntp.h" 13 #include "ntp_syslog.h" 14 #include "ntp_stdlib.h" 15 #include "ntp_keyacc.h" 16 17 #ifdef OPENSSL 18 #include "openssl/objects.h" 19 #include "openssl/evp.h" 20 #endif /* OPENSSL */ 21 22 /* Forwards */ 23 static char *nexttok (char **); 24 25 /* 26 * nexttok - basic internal tokenizing routine 27 */ 28 static char * 29 nexttok( 30 char **str 31 ) 32 { 33 register char *cp; 34 char *starttok; 35 36 cp = *str; 37 38 /* 39 * Space past white space 40 */ 41 while (*cp == ' ' || *cp == '\t') 42 cp++; 43 44 /* 45 * Save this and space to end of token 46 */ 47 starttok = cp; 48 while (*cp != '\0' && *cp != '\n' && *cp != ' ' 49 && *cp != '\t' && *cp != '#') 50 cp++; 51 52 /* 53 * If token length is zero return an error, else set end of 54 * token to zero and return start. 55 */ 56 if (starttok == cp) 57 return NULL; 58 59 if (*cp == ' ' || *cp == '\t') 60 *cp++ = '\0'; 61 else 62 *cp = '\0'; 63 64 *str = cp; 65 return starttok; 66 } 67 68 69 /* TALOS-CAN-0055: possibly DoS attack by setting the key file to the 70 * log file. This is hard to prevent (it would need to check two files 71 * to be the same on the inode level, which will not work so easily with 72 * Windows or VMS) but we can avoid the self-amplification loop: We only 73 * log the first 5 errors, silently ignore the next 10 errors, and give 74 * up when when we have found more than 15 errors. 75 * 76 * This avoids the endless file iteration we will end up with otherwise, 77 * and also avoids overflowing the log file. 78 * 79 * Nevertheless, once this happens, the keys are gone since this would 80 * require a save/swap strategy that is not easy to apply due to the 81 * data on global/static level. 82 */ 83 84 static const u_int nerr_loglimit = 5u; 85 static const u_int nerr_maxlimit = 15; 86 87 static void log_maybe(u_int*, const char*, ...) NTP_PRINTF(2, 3); 88 89 typedef struct keydata KeyDataT; 90 struct keydata { 91 KeyDataT *next; /* queue/stack link */ 92 KeyAccT *keyacclist; /* key access list */ 93 keyid_t keyid; /* stored key ID */ 94 u_short keytype; /* stored key type */ 95 u_short seclen; /* length of secret */ 96 u_char secbuf[1]; /* begin of secret (formal only)*/ 97 }; 98 99 static void 100 log_maybe( 101 u_int *pnerr, 102 const char *fmt , 103 ...) 104 { 105 va_list ap; 106 if ((NULL == pnerr) || (++(*pnerr) <= nerr_loglimit)) { 107 va_start(ap, fmt); 108 mvsyslog(LOG_ERR, fmt, ap); 109 va_end(ap); 110 } 111 } 112 113 static void 114 free_keydata( 115 KeyDataT *node 116 ) 117 { 118 KeyAccT *kap; 119 120 if (node) { 121 while (node->keyacclist) { 122 kap = node->keyacclist; 123 node->keyacclist = kap->next; 124 free(kap); 125 } 126 127 /* purge secrets from memory before free()ing it */ 128 memset(node, 0, sizeof(*node) + node->seclen); 129 free(node); 130 } 131 } 132 133 /* 134 * authreadkeys - (re)read keys from a file. 135 */ 136 int 137 authreadkeys( 138 const char *file 139 ) 140 { 141 FILE *fp; 142 char *line; 143 char *token; 144 keyid_t keyno; 145 int keytype; 146 char buf[512]; /* lots of room for line */ 147 u_char keystr[32]; /* Bug 2537 */ 148 size_t len; 149 size_t j; 150 u_int nerr; 151 KeyDataT *list = NULL; 152 KeyDataT *next = NULL; 153 154 /* 155 * Open file. Complain and return if it can't be opened. 156 */ 157 fp = fopen(file, "r"); 158 if (fp == NULL) { 159 msyslog(LOG_ERR, "authreadkeys: file '%s': %m", 160 file); 161 goto onerror; 162 } 163 INIT_SSL(); 164 165 /* 166 * Now read lines from the file, looking for key entries. Put 167 * the data into temporary store for later propagation to avoid 168 * two-pass processing. 169 */ 170 nerr = 0; 171 while ((line = fgets(buf, sizeof buf, fp)) != NULL) { 172 if (nerr > nerr_maxlimit) 173 break; 174 token = nexttok(&line); 175 if (token == NULL) 176 continue; 177 178 /* 179 * First is key number. See if it is okay. 180 */ 181 keyno = atoi(token); 182 if (keyno < 1) { 183 log_maybe(&nerr, 184 "authreadkeys: cannot change key %s", 185 token); 186 continue; 187 } 188 189 if (keyno > NTP_MAXKEY) { 190 log_maybe(&nerr, 191 "authreadkeys: key %s > %d reserved for Autokey", 192 token, NTP_MAXKEY); 193 continue; 194 } 195 196 /* 197 * Next is keytype. See if that is all right. 198 */ 199 token = nexttok(&line); 200 if (token == NULL) { 201 log_maybe(&nerr, 202 "authreadkeys: no key type for key %d", 203 keyno); 204 continue; 205 } 206 207 /* We want to silently ignore keys where we do not 208 * support the requested digest type. OTOH, we want to 209 * make sure the file is well-formed. That means we 210 * have to process the line completely and have to 211 * finally throw away the result... This is a bit more 212 * work, but it also results in better error detection. 213 */ 214 #ifdef OPENSSL 215 /* 216 * The key type is the NID used by the message digest 217 * algorithm. There are a number of inconsistencies in 218 * the OpenSSL database. We attempt to discover them 219 * here and prevent use of inconsistent data later. 220 */ 221 keytype = keytype_from_text(token, NULL); 222 if (keytype == 0) { 223 log_maybe(NULL, 224 "authreadkeys: invalid type for key %d", 225 keyno); 226 # ifdef ENABLE_CMAC 227 } else if (NID_cmac != keytype && 228 EVP_get_digestbynid(keytype) == NULL) { 229 log_maybe(NULL, 230 "authreadkeys: no algorithm for key %d", 231 keyno); 232 keytype = 0; 233 # endif /* ENABLE_CMAC */ 234 } 235 #else /* !OPENSSL follows */ 236 /* 237 * The key type is unused, but is required to be 'M' or 238 * 'm' for compatibility. 239 */ 240 if (!(*token == 'M' || *token == 'm')) { 241 log_maybe(NULL, 242 "authreadkeys: invalid type for key %d", 243 keyno); 244 keytype = 0; 245 } else { 246 keytype = KEY_TYPE_MD5; 247 } 248 #endif /* !OPENSSL */ 249 250 /* 251 * Finally, get key and insert it. If it is longer than 20 252 * characters, it is a binary string encoded in hex; 253 * otherwise, it is a text string of printable ASCII 254 * characters. 255 */ 256 token = nexttok(&line); 257 if (token == NULL) { 258 log_maybe(&nerr, 259 "authreadkeys: no key for key %d", keyno); 260 continue; 261 } 262 next = NULL; 263 len = strlen(token); 264 if (len <= 20) { /* Bug 2537 */ 265 next = emalloc(sizeof(KeyDataT) + len); 266 next->keyacclist = NULL; 267 next->keyid = keyno; 268 next->keytype = keytype; 269 next->seclen = len; 270 memcpy(next->secbuf, token, len); 271 } else { 272 static const char hex[] = "0123456789abcdef"; 273 u_char temp; 274 char *ptr; 275 size_t jlim; 276 277 jlim = min(len, 2 * sizeof(keystr)); 278 for (j = 0; j < jlim; j++) { 279 ptr = strchr(hex, tolower((unsigned char)token[j])); 280 if (ptr == NULL) 281 break; /* abort decoding */ 282 temp = (u_char)(ptr - hex); 283 if (j & 1) 284 keystr[j / 2] |= temp; 285 else 286 keystr[j / 2] = temp << 4; 287 } 288 if (j < jlim) { 289 log_maybe(&nerr, 290 "authreadkeys: invalid hex digit for key %d", 291 keyno); 292 continue; 293 } 294 len = jlim/2; /* hmmmm.... what about odd length?!? */ 295 next = emalloc(sizeof(KeyDataT) + len); 296 next->keyacclist = NULL; 297 next->keyid = keyno; 298 next->keytype = keytype; 299 next->seclen = len; 300 memcpy(next->secbuf, keystr, len); 301 } 302 303 token = nexttok(&line); 304 if (token != NULL) { /* A comma-separated IP access list */ 305 char *tp = token; 306 307 while (tp) { 308 char *i; 309 char *snp; /* subnet text pointer */ 310 unsigned int snbits; 311 sockaddr_u addr; 312 313 i = strchr(tp, (int)','); 314 if (i) { 315 *i = '\0'; 316 } 317 snp = strchr(tp, (int)'/'); 318 if (snp) { 319 char *sp; 320 321 *snp++ = '\0'; 322 snbits = 0; 323 sp = snp; 324 325 while (*sp != '\0') { 326 if (!isdigit((unsigned char)*sp)) 327 break; 328 if (snbits > 1000) 329 break; /* overflow */ 330 snbits = 10 * snbits + (*sp++ - '0'); /* ascii dependent */ 331 } 332 if (*sp != '\0') { 333 log_maybe(&nerr, 334 "authreadkeys: Invalid character in subnet specification for <%s/%s> in key %d", 335 sp, snp, keyno); 336 goto nextip; 337 } 338 } else { 339 snbits = UINT_MAX; 340 } 341 342 if (is_ip_address(tp, AF_UNSPEC, &addr)) { 343 /* Make sure that snbits is valid for addr */ 344 if ((snbits < UINT_MAX) && 345 ( (IS_IPV4(&addr) && snbits > 32) || 346 (IS_IPV6(&addr) && snbits > 128))) { 347 log_maybe(NULL, 348 "authreadkeys: excessive subnet mask <%s/%s> for key %d", 349 tp, snp, keyno); 350 } 351 next->keyacclist = keyacc_new_push( 352 next->keyacclist, &addr, snbits); 353 } else { 354 log_maybe(&nerr, 355 "authreadkeys: invalid IP address <%s> for key %d", 356 tp, keyno); 357 } 358 359 nextip: 360 if (i) { 361 tp = i + 1; 362 } else { 363 tp = 0; 364 } 365 } 366 } 367 368 /* check if this has to be weeded out... */ 369 if (0 == keytype) { 370 free_keydata(next); 371 next = NULL; 372 continue; 373 } 374 375 INSIST(NULL != next); 376 next->next = list; 377 list = next; 378 } 379 fclose(fp); 380 if (nerr > 0) { 381 const char * why = ""; 382 if (nerr > nerr_maxlimit) 383 why = " (emergency break)"; 384 msyslog(LOG_ERR, 385 "authreadkeys: rejecting file '%s' after %u error(s)%s", 386 file, nerr, why); 387 goto onerror; 388 } 389 390 /* first remove old file-based keys */ 391 auth_delkeys(); 392 /* insert the new key material */ 393 while (NULL != (next = list)) { 394 list = next->next; 395 MD5auth_setkey(next->keyid, next->keytype, 396 next->secbuf, next->seclen, next->keyacclist); 397 next->keyacclist = NULL; /* consumed by MD5auth_setkey */ 398 free_keydata(next); 399 } 400 return (1); 401 402 onerror: 403 /* Mop up temporary storage before bailing out. */ 404 while (NULL != (next = list)) { 405 list = next->next; 406 free_keydata(next); 407 } 408 return (0); 409 } 410