#include <config.h>
#include <stdio.h>
#include <ctype.h>
#include "ntpd.h"
#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
static char *nexttok (char **);
static char *
nexttok(
char **str
)
{
register char *cp;
char *starttok;
cp = *str;
while (*cp == ' ' || *cp == '\t')
cp++;
starttok = cp;
while (*cp != '\0' && *cp != '\n' && *cp != ' '
&& *cp != '\t' && *cp != '#')
cp++;
if (starttok == cp)
return NULL;
if (*cp == ' ' || *cp == '\t')
*cp++ = '\0';
else
*cp = '\0';
*str = cp;
return starttok;
}
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;
KeyAccT *keyacclist;
keyid_t keyid;
u_short keytype;
u_short seclen;
u_char secbuf[1];
};
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);
}
memset(node, 0, sizeof(*node) + node->seclen);
free(node);
}
}
int
authreadkeys(
const char *file
)
{
FILE *fp;
char *line;
char *token;
keyid_t keyno;
int keytype;
char buf[512];
u_char keystr[32];
size_t len;
size_t j;
u_int nerr;
KeyDataT *list = NULL;
KeyDataT *next = NULL;
fp = fopen(file, "r");
if (fp == NULL) {
msyslog(LOG_ERR, "authreadkeys: file '%s': %m",
file);
goto onerror;
}
INIT_SSL();
nerr = 0;
while ((line = fgets(buf, sizeof buf, fp)) != NULL) {
if (nerr > nerr_maxlimit)
break;
token = nexttok(&line);
if (token == NULL)
continue;
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;
}
token = nexttok(&line);
if (token == NULL) {
log_maybe(&nerr,
"authreadkeys: no key type for key %d",
keyno);
continue;
}
#ifdef OPENSSL
keytype = keytype_from_text(token, NULL);
if (keytype == 0) {
log_maybe(NULL,
"authreadkeys: invalid type for key %d",
keyno);
} else if (EVP_get_digestbynid(keytype) == NULL) {
log_maybe(NULL,
"authreadkeys: no algorithm for key %d",
keyno);
keytype = 0;
}
#else
if (!(*token == 'M' || *token == 'm')) {
log_maybe(NULL,
"authreadkeys: invalid type for key %d",
keyno);
keytype = 0;
} else {
keytype = KEY_TYPE_MD5;
}
#endif
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) {
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;
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;
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);
DPRINTF(0, ("authreadkeys: full access list <%s>\n", (token) ? token : "NULL"));
if (token != NULL) {
char *tp = token;
while (tp) {
char *i;
sockaddr_u addr;
i = strchr(tp, (int)',');
if (i)
*i = '\0';
DPRINTF(0, ("authreadkeys: access list: <%s>\n", tp));
if (is_ip_address(tp, AF_UNSPEC, &addr)) {
next->keyacclist = keyacc_new_push(
next->keyacclist, &addr);
} else {
log_maybe(&nerr,
"authreadkeys: invalid IP address <%s> for key %d",
tp, keyno);
}
if (i) {
tp = i + 1;
} else {
tp = 0;
}
}
}
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;
}
auth_delkeys();
while (NULL != (next = list)) {
list = next->next;
MD5auth_setkey(next->keyid, next->keytype,
next->secbuf, next->seclen, next->keyacclist);
next->keyacclist = NULL;
free_keydata(next);
}
return (1);
onerror:
while (NULL != (next = list)) {
list = next->next;
free_keydata(next);
}
return (0);
}