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