diff options
Diffstat (limited to 'sebhbsd/freebsd/contrib/ntp/libntp/authreadkeys.c')
-rw-r--r-- | sebhbsd/freebsd/contrib/ntp/libntp/authreadkeys.c | 409 |
1 files changed, 409 insertions, 0 deletions
diff --git a/sebhbsd/freebsd/contrib/ntp/libntp/authreadkeys.c b/sebhbsd/freebsd/contrib/ntp/libntp/authreadkeys.c new file mode 100644 index 0000000..f0b878a --- /dev/null +++ b/sebhbsd/freebsd/contrib/ntp/libntp/authreadkeys.c @@ -0,0 +1,409 @@ +#include <machine/rtems-bsd-user-space.h> + +/* + * authreadkeys.c - routines to support the reading of the key file + */ +#include <config.h> +#include <stdio.h> +#include <ctype.h> + +//#include "ntpd.h" /* Only for DPRINTF */ +//#include "ntp_fp.h" +#include "ntp.h" +#include "ntp_syslog.h" +#include "ntp_stdlib.h" +#include "ntp_keyacc.h" + +#ifdef OPENSSL +#include "openssl/objects.h" +#include "openssl/evp.h" +#endif /* OPENSSL */ + +/* Forwards */ +static char *nexttok (char **); + +/* + * nexttok - basic internal tokenizing routine + */ +static char * +nexttok( + char **str + ) +{ + register char *cp; + char *starttok; + + cp = *str; + + /* + * Space past white space + */ + while (*cp == ' ' || *cp == '\t') + cp++; + + /* + * Save this and space to end of token + */ + starttok = cp; + while (*cp != '\0' && *cp != '\n' && *cp != ' ' + && *cp != '\t' && *cp != '#') + cp++; + + /* + * If token length is zero return an error, else set end of + * token to zero and return start. + */ + if (starttok == cp) + return NULL; + + if (*cp == ' ' || *cp == '\t') + *cp++ = '\0'; + else + *cp = '\0'; + + *str = cp; + return starttok; +} + + +/* TALOS-CAN-0055: possibly DoS attack by setting the key file to the + * log file. This is hard to prevent (it would need to check two files + * to be the same on the inode level, which will not work so easily with + * Windows or VMS) but we can avoid the self-amplification loop: We only + * log the first 5 errors, silently ignore the next 10 errors, and give + * up when when we have found more than 15 errors. + * + * This avoids the endless file iteration we will end up with otherwise, + * and also avoids overflowing the log file. + * + * Nevertheless, once this happens, the keys are gone since this would + * require a save/swap strategy that is not easy to apply due to the + * data on global/static level. + */ + +static const u_int nerr_loglimit = 5u; +static const u_int nerr_maxlimit = 15; + +static void log_maybe(u_int*, const char*, ...) NTP_PRINTF(2, 3); + +typedef struct keydata KeyDataT; +struct keydata { + KeyDataT *next; /* queue/stack link */ + KeyAccT *keyacclist; /* key access list */ + keyid_t keyid; /* stored key ID */ + u_short keytype; /* stored key type */ + u_short seclen; /* length of secret */ + u_char secbuf[1]; /* begin of secret (formal only)*/ +}; + +static void +log_maybe( + u_int *pnerr, + const char *fmt , + ...) +{ + va_list ap; + if ((NULL == pnerr) || (++(*pnerr) <= nerr_loglimit)) { + va_start(ap, fmt); + mvsyslog(LOG_ERR, fmt, ap); + va_end(ap); + } +} + +static void +free_keydata( + KeyDataT *node + ) +{ + KeyAccT *kap; + + if (node) { + while (node->keyacclist) { + kap = node->keyacclist; + node->keyacclist = kap->next; + free(kap); + } + + /* purge secrets from memory before free()ing it */ + memset(node, 0, sizeof(*node) + node->seclen); + free(node); + } +} + +/* + * authreadkeys - (re)read keys from a file. + */ +int +authreadkeys( + const char *file + ) +{ + FILE *fp; + char *line; + char *token; + keyid_t keyno; + int keytype; + char buf[512]; /* lots of room for line */ + u_char keystr[32]; /* Bug 2537 */ + size_t len; + size_t j; + u_int nerr; + KeyDataT *list = NULL; + KeyDataT *next = NULL; + + /* + * Open file. Complain and return if it can't be opened. + */ + fp = fopen(file, "r"); + if (fp == NULL) { + msyslog(LOG_ERR, "authreadkeys: file '%s': %m", + file); + goto onerror; + } + INIT_SSL(); + + /* + * Now read lines from the file, looking for key entries. Put + * the data into temporary store for later propagation to avoid + * two-pass processing. + */ + nerr = 0; + while ((line = fgets(buf, sizeof buf, fp)) != NULL) { + if (nerr > nerr_maxlimit) + break; + token = nexttok(&line); + if (token == NULL) + continue; + + /* + * First is key number. See if it is okay. + */ + keyno = atoi(token); + if (keyno < 1) { + log_maybe(&nerr, + "authreadkeys: cannot change key %s", + token); + continue; + } + + if (keyno > NTP_MAXKEY) { + log_maybe(&nerr, + "authreadkeys: key %s > %d reserved for Autokey", + token, NTP_MAXKEY); + continue; + } + + /* + * Next is keytype. See if that is all right. + */ + token = nexttok(&line); + if (token == NULL) { + log_maybe(&nerr, + "authreadkeys: no key type for key %d", + keyno); + continue; + } + + /* We want to silently ignore keys where we do not + * support the requested digest type. OTOH, we want to + * make sure the file is well-formed. That means we + * have to process the line completely and have to + * finally throw away the result... This is a bit more + * work, but it also results in better error detection. + */ +#ifdef OPENSSL + /* + * The key type is the NID used by the message digest + * algorithm. There are a number of inconsistencies in + * the OpenSSL database. We attempt to discover them + * here and prevent use of inconsistent data later. + */ + keytype = keytype_from_text(token, NULL); + if (keytype == 0) { + log_maybe(NULL, + "authreadkeys: invalid type for key %d", + keyno); +# ifdef ENABLE_CMAC + } else if (NID_cmac != keytype && + EVP_get_digestbynid(keytype) == NULL) { + log_maybe(NULL, + "authreadkeys: no algorithm for key %d", + keyno); + keytype = 0; +# endif /* ENABLE_CMAC */ + } +#else /* !OPENSSL follows */ + /* + * The key type is unused, but is required to be 'M' or + * 'm' for compatibility. + */ + if (!(*token == 'M' || *token == 'm')) { + log_maybe(NULL, + "authreadkeys: invalid type for key %d", + keyno); + keytype = 0; + } else { + keytype = KEY_TYPE_MD5; + } +#endif /* !OPENSSL */ + + /* + * Finally, get key and insert it. If it is longer than 20 + * characters, it is a binary string encoded in hex; + * otherwise, it is a text string of printable ASCII + * characters. + */ + token = nexttok(&line); + if (token == NULL) { + log_maybe(&nerr, + "authreadkeys: no key for key %d", keyno); + continue; + } + next = NULL; + len = strlen(token); + if (len <= 20) { /* Bug 2537 */ + next = emalloc(sizeof(KeyDataT) + len); + next->keyacclist = NULL; + next->keyid = keyno; + next->keytype = keytype; + next->seclen = len; + memcpy(next->secbuf, token, len); + } else { + static const char hex[] = "0123456789abcdef"; + u_char temp; + char *ptr; + size_t jlim; + + jlim = min(len, 2 * sizeof(keystr)); + for (j = 0; j < jlim; j++) { + ptr = strchr(hex, tolower((unsigned char)token[j])); + if (ptr == NULL) + break; /* abort decoding */ + temp = (u_char)(ptr - hex); + if (j & 1) + keystr[j / 2] |= temp; + else + keystr[j / 2] = temp << 4; + } + if (j < jlim) { + log_maybe(&nerr, + "authreadkeys: invalid hex digit for key %d", + keyno); + continue; + } + len = jlim/2; /* hmmmm.... what about odd length?!? */ + next = emalloc(sizeof(KeyDataT) + len); + next->keyacclist = NULL; + next->keyid = keyno; + next->keytype = keytype; + next->seclen = len; + memcpy(next->secbuf, keystr, len); + } + + token = nexttok(&line); + if (token != NULL) { /* A comma-separated IP access list */ + char *tp = token; + + while (tp) { + char *i; + char *snp; /* subnet text pointer */ + unsigned int snbits; + sockaddr_u addr; + + i = strchr(tp, (int)','); + if (i) { + *i = '\0'; + } + snp = strchr(tp, (int)'/'); + if (snp) { + char *sp; + + *snp++ = '\0'; + snbits = 0; + sp = snp; + + while (*sp != '\0') { + if (!isdigit((unsigned char)*sp)) + break; + if (snbits > 1000) + break; /* overflow */ + snbits = 10 * snbits + (*sp++ - '0'); /* ascii dependent */ + } + if (*sp != '\0') { + log_maybe(&nerr, + "authreadkeys: Invalid character in subnet specification for <%s/%s> in key %d", + sp, snp, keyno); + goto nextip; + } + } else { + snbits = UINT_MAX; + } + + if (is_ip_address(tp, AF_UNSPEC, &addr)) { + /* Make sure that snbits is valid for addr */ + if ((snbits < UINT_MAX) && + ( (IS_IPV4(&addr) && snbits > 32) || + (IS_IPV6(&addr) && snbits > 128))) { + log_maybe(NULL, + "authreadkeys: excessive subnet mask <%s/%s> for key %d", + tp, snp, keyno); + } + next->keyacclist = keyacc_new_push( + next->keyacclist, &addr, snbits); + } else { + log_maybe(&nerr, + "authreadkeys: invalid IP address <%s> for key %d", + tp, keyno); + } + + nextip: + if (i) { + tp = i + 1; + } else { + tp = 0; + } + } + } + + /* check if this has to be weeded out... */ + if (0 == keytype) { + free_keydata(next); + next = NULL; + continue; + } + + INSIST(NULL != next); + next->next = list; + list = next; + } + fclose(fp); + if (nerr > 0) { + const char * why = ""; + if (nerr > nerr_maxlimit) + why = " (emergency break)"; + msyslog(LOG_ERR, + "authreadkeys: rejecting file '%s' after %u error(s)%s", + file, nerr, why); + goto onerror; + } + + /* first remove old file-based keys */ + auth_delkeys(); + /* insert the new key material */ + while (NULL != (next = list)) { + list = next->next; + MD5auth_setkey(next->keyid, next->keytype, + next->secbuf, next->seclen, next->keyacclist); + next->keyacclist = NULL; /* consumed by MD5auth_setkey */ + free_keydata(next); + } + return (1); + + onerror: + /* Mop up temporary storage before bailing out. */ + while (NULL != (next = list)) { + list = next->next; + free_keydata(next); + } + return (0); +} |