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 ((NULL == pnerr) || (++(*pnerr) <= nerr_loglimit)) { 105 va_start(ap, fmt); 106 mvsyslog(LOG_ERR, fmt, ap); 107 va_end(ap); 108 } 109 } 110 111 static void 112 free_keydata( 113 KeyDataT *node 114 ) 115 { 116 KeyAccT *kap; 117 118 if (node) { 119 while (node->keyacclist) { 120 kap = node->keyacclist; 121 node->keyacclist = kap->next; 122 free(kap); 123 } 124 125 /* purge secrets from memory before free()ing it */ 126 memset(node, 0, sizeof(*node) + node->seclen); 127 free(node); 128 } 129 } 130 131 /* 132 * authreadkeys - (re)read keys from a file. 133 */ 134 int 135 authreadkeys( 136 const char *file 137 ) 138 { 139 FILE *fp; 140 char *line; 141 char *token; 142 keyid_t keyno; 143 int keytype; 144 char buf[512]; /* lots of room for line */ 145 u_char keystr[AUTHPWD_MAXSECLEN]; 146 size_t len; 147 u_int nerr; 148 KeyDataT *list = NULL; 149 KeyDataT *next = NULL; 150 151 /* 152 * Open file. Complain and return if it can't be opened. 153 */ 154 fp = fopen(file, "r"); 155 if (fp == NULL) { 156 msyslog(LOG_ERR, "authreadkeys: file '%s': %m", 157 file); 158 goto onerror; 159 } 160 INIT_SSL(); 161 162 /* 163 * Now read lines from the file, looking for key entries. Put 164 * the data into temporary store for later propagation to avoid 165 * two-pass processing. 166 */ 167 nerr = 0; 168 while ((line = fgets(buf, sizeof buf, fp)) != NULL) { 169 if (nerr > nerr_maxlimit) 170 break; 171 token = nexttok(&line); 172 if (token == NULL) 173 continue; 174 175 /* 176 * First is key number. See if it is okay. 177 */ 178 keyno = atoi(token); 179 if (keyno < 1) { 180 log_maybe(&nerr, 181 "authreadkeys: cannot change key %s", 182 token); 183 continue; 184 } 185 186 if (keyno > NTP_MAXKEY) { 187 log_maybe(&nerr, 188 "authreadkeys: key %s > %d reserved for Autokey", 189 token, NTP_MAXKEY); 190 continue; 191 } 192 193 /* 194 * Next is keytype. See if that is all right. 195 */ 196 token = nexttok(&line); 197 if (token == NULL) { 198 log_maybe(&nerr, 199 "authreadkeys: no key type for key %d", 200 keyno); 201 continue; 202 } 203 204 /* We want to silently ignore keys where we do not 205 * support the requested digest type. OTOH, we want to 206 * make sure the file is well-formed. That means we 207 * have to process the line completely and have to 208 * finally throw away the result... This is a bit more 209 * work, but it also results in better error detection. 210 */ 211 #ifdef OPENSSL 212 /* 213 * The key type is the NID used by the message digest 214 * algorithm. There are a number of inconsistencies in 215 * the OpenSSL database. We attempt to discover them 216 * here and prevent use of inconsistent data later. 217 */ 218 keytype = keytype_from_text(token, NULL); 219 if (keytype == 0) { 220 log_maybe(NULL, 221 "authreadkeys: unsupported type %s for key %d", 222 token, keyno); 223 # ifdef ENABLE_CMAC 224 } else if (NID_cmac != keytype && 225 EVP_get_digestbynid(keytype) == NULL) { 226 log_maybe(NULL, 227 "authreadkeys: no algorithm for %s key %d", 228 token, keyno); 229 keytype = 0; 230 # endif /* ENABLE_CMAC */ 231 } 232 #else /* !OPENSSL follows */ 233 /* 234 * The key type is unused, but is required to be 'M' or 235 * 'm' for compatibility. 236 */ 237 if (! (toupper(*token) == 'M')) { 238 log_maybe(NULL, 239 "authreadkeys: invalid type for key %d", 240 keyno); 241 keytype = 0; 242 } else { 243 keytype = KEY_TYPE_MD5; 244 } 245 #endif /* !OPENSSL */ 246 247 /* 248 * Finally, get key and insert it. If it is longer than 20 249 * characters, it is a binary string encoded in hex; 250 * otherwise, it is a text string of printable ASCII 251 * characters. 252 */ 253 token = nexttok(&line); 254 if (token == NULL) { 255 log_maybe(&nerr, 256 "authreadkeys: no key for key %d", keyno); 257 continue; 258 } 259 next = NULL; 260 len = authdecodepw(keystr, sizeof(keystr), token, AUTHPWD_UNSPEC); 261 if (len > sizeof(keystr)) { 262 switch (errno) { 263 case ENOMEM: 264 log_maybe(&nerr, 265 "authreadkeys: passwd too long for key %d", 266 keyno); 267 break; 268 case EINVAL: 269 log_maybe(&nerr, 270 "authreadkeys: passwd has bad char for key %d", 271 keyno); 272 break; 273 #ifdef DEBUG 274 default: 275 log_maybe(&nerr, 276 "authreadkeys: unexpected errno %d for key %d: %m", 277 errno, keyno); 278 break; 279 #endif 280 } 281 continue; 282 } 283 next = emalloc(sizeof(KeyDataT) + len); 284 next->keyacclist = NULL; 285 next->keyid = keyno; 286 next->keytype = keytype; 287 next->seclen = len; 288 memcpy(next->secbuf, keystr, len); 289 290 token = nexttok(&line); 291 if (token != NULL) { /* A comma-separated IP access list */ 292 char *tp = token; 293 294 while (tp) { 295 char *i; 296 char *snp; /* subnet text pointer */ 297 unsigned int snbits; 298 sockaddr_u addr; 299 300 i = strchr(tp, (int)','); 301 if (i) { 302 *i = '\0'; 303 } 304 snp = strchr(tp, (int)'/'); 305 if (snp) { 306 char *sp; 307 308 *snp++ = '\0'; 309 snbits = 0; 310 sp = snp; 311 312 while (*sp != '\0') { 313 if (!isdigit((unsigned char)*sp)) 314 break; 315 if (snbits > 1000) 316 break; /* overflow */ 317 snbits = 10 * snbits + (*sp++ - '0'); /* ascii dependent */ 318 } 319 if (*sp != '\0') { 320 log_maybe(&nerr, 321 "authreadkeys: Invalid character in subnet specification for <%s/%s> in key %d", 322 sp, snp, keyno); 323 goto nextip; 324 } 325 } else { 326 snbits = UINT_MAX; 327 } 328 329 if (is_ip_address(tp, AF_UNSPEC, &addr)) { 330 /* Make sure that snbits is valid for addr */ 331 if ((snbits < UINT_MAX) && 332 ( (IS_IPV4(&addr) && snbits > 32) || 333 (IS_IPV6(&addr) && snbits > 128))) { 334 log_maybe(NULL, 335 "authreadkeys: excessive subnet mask <%s/%s> for key %d", 336 tp, snp, keyno); 337 } 338 next->keyacclist = keyacc_new_push( 339 next->keyacclist, &addr, snbits); 340 } else { 341 log_maybe(&nerr, 342 "authreadkeys: invalid IP address <%s> for key %d", 343 tp, keyno); 344 } 345 346 nextip: 347 if (i) { 348 tp = i + 1; 349 } else { 350 tp = 0; 351 } 352 } 353 } 354 355 /* check if this has to be weeded out... */ 356 if (0 == keytype) { 357 free_keydata(next); 358 next = NULL; 359 continue; 360 } 361 362 DEBUG_INSIST(NULL != next); 363 #if defined(OPENSSL) && defined(ENABLE_CMAC) 364 if (NID_cmac == keytype && len < 16) { 365 msyslog(LOG_WARNING, CMAC " keys are 128 bits, " 366 "zero-extending key %u by %u bits", 367 (u_int)keyno, 8 * (16 - (u_int)len)); 368 } 369 #endif /* OPENSSL && ENABLE_CMAC */ 370 next->next = list; 371 list = next; 372 } 373 fclose(fp); 374 if (nerr > 0) { 375 const char * why = ""; 376 377 if (nerr > nerr_maxlimit) 378 why = " (emergency break)"; 379 msyslog(LOG_ERR, 380 "authreadkeys: rejecting file '%s' after %u error(s)%s", 381 file, nerr, why); 382 goto onerror; 383 } 384 385 /* first remove old file-based keys */ 386 auth_delkeys(); 387 /* insert the new key material */ 388 while (NULL != (next = list)) { 389 list = next->next; 390 MD5auth_setkey(next->keyid, next->keytype, 391 next->secbuf, next->seclen, next->keyacclist); 392 next->keyacclist = NULL; /* consumed by MD5auth_setkey */ 393 free_keydata(next); 394 } 395 return (1); 396 397 onerror: 398 /* Mop up temporary storage before bailing out. */ 399 while (NULL != (next = list)) { 400 list = next->next; 401 free_keydata(next); 402 } 403 return (0); 404 } 405