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