#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#ifdef AUTOKEY
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <fcntl.h>
#include "ntpd.h"
#include "ntp_stdlib.h"
#include "ntp_string.h"
#include "ntp_crypto.h"
#ifdef KERNEL_PLL
#include "ntp_syscall.h"
#endif
#define COOKIE_LEN (5 * 4)
#define AUTOKEY_LEN (6 * 4)
#define VALUE_LEN (6 * 4)
u_int crypto_flags;
u_int sys_tai;
#ifdef PUBKEY
#define TAI_1972 10
#define MAX_LEAP 100
#define MAX_LINLEN 1024
#define MAX_KEYLEN 1024
#define MAX_ENCLEN (ENCODED_CONTENT_LEN(1024))
static R_RSA_PRIVATE_KEY private_key;
static R_RSA_PUBLIC_KEY public_key;
static R_DH_PARAMS dh_params;
static u_char *dh_private;
static u_int dh_keyLen;
static char *keysdir = NTP_KEYSDIR;
static char *private_key_file = NULL;
static char *public_key_file = NULL;
static char *certif_file = NULL;
static char *dh_params_file = NULL;
static char *tai_leap_file = NULL;
struct value host;
struct value certif;
struct value dhparam;
struct value dhpub;
struct value tai_leap;
static u_int crypto_rsa P((char *, u_char *, u_int));
static void crypto_cert P((char *));
static void crypto_dh P((char *));
static void crypto_tai P((char *));
#endif
#define RV_OK 0
#define RV_LEN 1
#define RV_TSP 2
#define RV_FSP 3
#define RV_PUB 4
#define RV_KEY 5
#define RV_SIG 6
#define RV_DH 7
#define RV_FIL 8
#define RV_DAT 9
#define RV_DEC 10
#define RV_DUP 11
#define RV_VN 12
keyid_t
session_key(
struct sockaddr_in *srcadr,
struct sockaddr_in *dstadr,
keyid_t keyno,
keyid_t private,
u_long lifetime
)
{
MD5_CTX ctx;
keyid_t keyid;
u_int32 header[4];
u_char digest[16];
header[0] = srcadr->sin_addr.s_addr;
header[1] = dstadr->sin_addr.s_addr;
header[2] = htonl(keyno);
header[3] = htonl(private);
MD5Init(&ctx);
MD5Update(&ctx, (u_char *)header, sizeof(header));
MD5Final(digest, &ctx);
memcpy(&keyid, digest, 4);
keyid = ntohl(keyid);
if (lifetime != 0) {
MD5auth_setkey(keyno, digest, 16);
authtrust(keyno, lifetime);
}
#ifdef DEBUG
if (debug > 1)
printf(
"session_key: %s > %s %08x %08x hash %08x life %lu\n",
numtoa(header[0]), numtoa(header[1]), keyno,
private, keyid, lifetime);
#endif
return (keyid);
}
void
make_keylist(
struct peer *peer,
struct interface *dstadr
)
{
struct autokey *ap;
keyid_t keyid;
keyid_t cookie;
l_fp tstamp;
u_long ltemp;
int i;
#ifdef PUBKEY
R_SIGNATURE_CTX ctx;
int rval;
u_int len;
#endif
L_CLR(&tstamp);
if (sys_leap != LEAP_NOTINSYNC)
get_systime(&tstamp);
if (peer->keylist == NULL)
peer->keylist = (keyid_t *)emalloc(sizeof(keyid_t) *
NTP_MAXSESSION);
while (1) {
keyid = (u_long)RANDOM & 0xffffffff;
if (keyid <= NTP_MAXKEY)
continue;
if (authhavekey(keyid))
continue;
break;
}
ltemp = min(sys_automax, NTP_MAXSESSION * (1 << (peer->kpoll)));
peer->hcookie = session_key(&dstadr->sin, &peer->srcadr, 0,
sys_private, 0);
if (peer->hmode == MODE_BROADCAST)
cookie = 0;
else
cookie = peer->pcookie.key;
for (i = 0; i < NTP_MAXSESSION; i++) {
peer->keylist[i] = keyid;
peer->keynumber = i;
keyid = session_key(&dstadr->sin, &peer->srcadr, keyid,
cookie, ltemp);
ltemp -= 1 << peer->kpoll;
if (auth_havekey(keyid) || keyid <= NTP_MAXKEY ||
ltemp <= (1 << (peer->kpoll)))
break;
}
ap = &peer->sndauto;
ap->tstamp = htonl(tstamp.l_ui);
ap->seq = htonl(peer->keynumber);
ap->key = htonl(keyid);
ap->siglen = 0;
#if DEBUG
if (debug)
printf("make_keys: %d %08x %08x ts %u poll %d\n",
ntohl(ap->seq), ntohl(ap->key), cookie,
ntohl(ap->tstamp), peer->kpoll);
#endif
#ifdef PUBKEY
if(!crypto_flags)
return;
if (ap->sig == NULL)
ap->sig = emalloc(private_key.bits / 8);
EVP_SignInit(&ctx, DA_MD5);
EVP_SignUpdate(&ctx, (u_char *)ap, 12);
rval = EVP_SignFinal(&ctx, ap->sig, &len, &private_key);
if (rval != RV_OK)
msyslog(LOG_ERR, "crypto: keylist signature fails %x",
rval);
else
ap->siglen = htonl(len);
peer->flags |= FLAG_ASSOC;
#endif
}
void
crypto_recv(
struct peer *peer,
struct recvbuf *rbufp
)
{
u_int32 *pkt;
struct autokey *ap;
struct cookie *cp;
int has_mac;
int authlen;
int len;
u_int code;
tstamp_t tstamp;
int i, rval;
u_int temp;
#ifdef PUBKEY
R_SIGNATURE_CTX ctx;
struct value *vp;
u_char dh_key[MAX_KEYLEN];
R_RSA_PUBLIC_KEY *kp;
tstamp_t fstamp;
u_int32 *pp;
u_int rsalen = sizeof(R_RSA_PUBLIC_KEY) - sizeof(u_int) + 4;
u_int bits;
int j;
#ifdef KERNEL_PLL
#if NTP_API > 3
struct timex ntv;
#endif
#endif
#endif
pkt = (u_int32 *)&rbufp->recv_pkt;
authlen = LEN_PKT_NOMAC;
while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) {
i = authlen / 4;
len = ntohl(pkt[i]) & 0xffff;
code = (ntohl(pkt[i]) >> 16) & 0xffff;
temp = (code >> 8) & 0x3f;
if (temp != CRYPTO_VN) {
sys_unknownversion++;
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: incorrect version %d should be %d\n",
temp, CRYPTO_VN);
#endif
return;
}
tstamp = ntohl(pkt[i + 2]);
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: ext offset %d len %d code %x assocID %d\n",
authlen, len, code, (u_int32)ntohl(pkt[i +
1]));
#endif
switch (code) {
case CRYPTO_ASSOC | CRYPTO_RESP:
cp = (struct cookie *)&pkt[i + 2];
temp = ntohl(cp->key);
if (len < COOKIE_LEN) {
rval = RV_LEN;
} else if (tstamp == 0) {
rval = RV_TSP;
} else {
if (!peer->crypto)
peer->crypto = temp;
if (ntohl(pkt[i + 1]) != 0)
peer->assoc = ntohl(pkt[i + 1]);
rval = RV_OK;
}
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %d flags 0x%x ts %u\n",
rval, temp, tstamp);
#endif
break;
case CRYPTO_AUTO | CRYPTO_RESP:
if (!(peer->flags & FLAG_AUTOKEY) &&
ntohl(pkt[i + 1]) != 0)
peer->assoc = ntohl(pkt[i + 1]);
ap = (struct autokey *)&pkt[i + 2];
#ifdef PUBKEY
temp = ntohl(ap->siglen);
kp = (R_RSA_PUBLIC_KEY *)peer->pubkey.ptr;
if (len < AUTOKEY_LEN) {
rval = RV_LEN;
} else if (tstamp == 0 || tstamp <
peer->recauto.tstamp || (tstamp ==
peer->recauto.tstamp && (peer->flags &
FLAG_AUTOKEY))) {
rval = RV_TSP;
} else if (!crypto_flags) {
rval = RV_OK;
} else if (kp == NULL) {
rval = RV_PUB;
} else if (temp != kp->bits / 8) {
rval = RV_SIG;
} else {
EVP_VerifyInit(&ctx, DA_MD5);
EVP_VerifyUpdate(&ctx, (u_char *)ap,
12);
rval = EVP_VerifyFinal(&ctx,
(u_char *)ap->pkt, temp, kp);
}
#else
if (tstamp < peer->recauto.tstamp || (tstamp ==
peer->recauto.tstamp && (peer->flags &
FLAG_AUTOKEY)))
rval = RV_TSP;
else
rval = RV_OK;
#endif
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %x autokey %d %08x ts %u (%u)\n",
rval, ntohl(ap->seq),
ntohl(ap->key), tstamp,
peer->recauto.tstamp);
#endif
if (rval != RV_OK) {
if (rval != RV_TSP)
msyslog(LOG_ERR,
"crypto: %x autokey %d %08x ts %u (%u)\n",
rval, ntohl(ap->seq),
ntohl(ap->key), tstamp,
peer->recauto.tstamp);
break;
}
peer->flags |= FLAG_AUTOKEY;
peer->flash &= ~TEST10;
peer->assoc = ntohl(pkt[i + 1]);
peer->recauto.tstamp = tstamp;
peer->recauto.seq = ntohl(ap->seq);
peer->recauto.key = ntohl(ap->key);
peer->pkeyid = peer->recauto.key;
break;
case CRYPTO_PRIV:
peer->cmmd = ntohl(pkt[i]);
case CRYPTO_PRIV | CRYPTO_RESP:
cp = (struct cookie *)&pkt[i + 2];
#ifdef PUBKEY
temp = ntohl(cp->siglen);
kp = (R_RSA_PUBLIC_KEY *)peer->pubkey.ptr;
if (len < COOKIE_LEN) {
rval = RV_LEN;
} else if (tstamp == 0 || tstamp <
peer->pcookie.tstamp || (tstamp ==
peer->pcookie.tstamp && (peer->flags &
FLAG_AUTOKEY))) {
rval = RV_TSP;
} else if (!crypto_flags) {
rval = RV_OK;
} else if (kp == NULL) {
rval = RV_PUB;
} else if (temp != kp->bits / 8) {
rval = RV_SIG;
} else {
EVP_VerifyInit(&ctx, DA_MD5);
EVP_VerifyUpdate(&ctx, (u_char *)cp, 8);
rval = EVP_VerifyFinal(&ctx,
(u_char *)cp->pkt, temp, kp);
}
#else
if (tstamp <= peer->pcookie.tstamp || (tstamp ==
peer->pcookie.tstamp && (peer->flags &
FLAG_AUTOKEY)))
rval = RV_TSP;
else
rval = RV_OK;
#endif
if (peer->hmode == MODE_CLIENT)
temp = ntohl(cp->key);
else
temp = ntohl(cp->key) ^ peer->hcookie;
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %x cookie %08x ts %u (%u)\n",
rval, temp, tstamp,
peer->pcookie.tstamp);
#endif
if (rval != RV_OK) {
if (rval != RV_TSP)
msyslog(LOG_ERR,
"crypto: %x cookie %08x ts %u (%u)\n",
rval, temp, tstamp,
peer->pcookie.tstamp);
peer->cmmd |= CRYPTO_ERROR;
break;
}
if (!(peer->cast_flags & MDF_BCLNT))
peer->flags |= FLAG_AUTOKEY;
peer->flash &= ~TEST10;
peer->assoc = ntohl(pkt[i + 1]);
peer->pcookie.tstamp = tstamp;
if (temp != peer->pcookie.key) {
peer->pcookie.key = temp;
key_expire(peer);
}
break;
#ifdef PUBKEY
case CRYPTO_NAME | CRYPTO_RESP:
if (!crypto_flags)
break;
vp = (struct value *)&pkt[i + 2];
fstamp = ntohl(vp->fstamp);
temp = ntohl(vp->vallen);
j = i + 5 + ntohl(vp->vallen) / 4;
bits = ntohl(pkt[i + 5]);
if (len < VALUE_LEN) {
rval = RV_LEN;
} else if (temp < rsalen || bits <
MIN_RSA_MODULUS_BITS || bits >
MAX_RSA_MODULUS_BITS) {
rval = RV_KEY;
} else if (ntohl(pkt[j]) != bits / 8) {
rval = RV_SIG;
} else if (tstamp == 0 || tstamp <
peer->pubkey.tstamp || (tstamp ==
peer->pubkey.tstamp && (peer->flags &
FLAG_AUTOKEY))) {
rval = RV_TSP;
} else if (tstamp < peer->pubkey.fstamp ||
fstamp < peer->pubkey.fstamp) {
rval = RV_FSP;
} else if (fstamp == peer->pubkey.fstamp &&
(peer->flags & FLAG_AUTOKEY)) {
rval = RV_FSP;
} else {
EVP_VerifyInit(&ctx, DA_MD5);
EVP_VerifyUpdate(&ctx, (u_char *)vp,
temp + 12);
kp = emalloc(sizeof(R_RSA_PUBLIC_KEY));
kp->bits = bits;
memcpy(kp->modulus, &pkt[i + 6],
rsalen - 4);
rval = EVP_VerifyFinal(&ctx,
(u_char *)&pkt[j + 1],
ntohl(pkt[j]), kp);
if (rval != 0) {
free(kp);
} else {
j = i + 5 + rsalen / 4;
peer->pubkey.ptr = (u_char *)kp;
temp = strlen((char *)&pkt[j]);
peer->keystr = emalloc(temp +
1);
strcpy(peer->keystr,
(char *)&pkt[j]);
peer->pubkey.tstamp = tstamp;
peer->pubkey.fstamp = fstamp;
peer->flash &= ~TEST10;
if (!(peer->crypto &
CRYPTO_FLAG_CERT))
peer->flags |=
FLAG_PROVEN;
}
}
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %x host %s ts %u fs %u\n",
rval, (char *)&pkt[i + 5 + rsalen /
4], tstamp, fstamp);
#endif
if (rval != RV_OK) {
if (rval != RV_TSP)
msyslog(LOG_ERR,
"crypto: %x host %s ts %u fs %u\n",
rval, (char *)&pkt[i + 5 +
rsalen / 4], tstamp,
fstamp);
}
break;
case CRYPTO_CERT | CRYPTO_RESP:
if (!crypto_flags)
break;
vp = (struct value *)&pkt[i + 2];
fstamp = ntohl(vp->fstamp);
temp = ntohl(vp->vallen);
kp = (R_RSA_PUBLIC_KEY *)peer->pubkey.ptr;
j = i + 5 + temp / 4;
if (len < VALUE_LEN) {
rval = RV_LEN;
} else if (kp == NULL) {
rval = RV_PUB;
} else if (ntohl(pkt[j]) != kp->bits / 8) {
rval = RV_SIG;
} else if (tstamp == 0) {
rval = RV_TSP;
} else if (tstamp <
ntohl(peer->certif.fstamp) || fstamp <
ntohl(peer->certif.fstamp)) {
rval = RV_FSP;
} else if (fstamp ==
ntohl(peer->certif.fstamp) && (peer->flags &
FLAG_AUTOKEY)) {
peer->crypto &= ~CRYPTO_FLAG_CERT;
rval = RV_FSP;
} else {
EVP_VerifyInit(&ctx, DA_MD5);
EVP_VerifyUpdate(&ctx, (u_char *)vp,
temp + 12);
rval = EVP_VerifyFinal(&ctx,
(u_char *)&pkt[j + 1],
ntohl(pkt[j]), kp);
}
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %x certificate %u ts %u fs %u\n",
rval, temp, tstamp, fstamp);
#endif
if (rval != RV_OK || temp == 0) {
if (rval != RV_TSP)
msyslog(LOG_ERR,
"crypto: %x certificate %u ts %u fs %u\n",
rval, temp, tstamp, fstamp);
break;
}
peer->flash &= ~TEST10;
peer->flags |= FLAG_PROVEN;
peer->crypto &= ~CRYPTO_FLAG_CERT;
peer->certif.tstamp = vp->tstamp;
peer->certif.fstamp = vp->fstamp;
peer->certif.vallen = vp->vallen;
if (peer->certif.ptr == NULL)
free(peer->certif.ptr);
peer->certif.ptr = emalloc(temp);
memcpy(peer->certif.ptr, vp->pkt, temp);
crypto_agree();
break;
case CRYPTO_DHPAR | CRYPTO_RESP:
if (!crypto_flags)
break;
vp = (struct value *)&pkt[i + 2];
fstamp = ntohl(vp->fstamp);
temp = ntohl(vp->vallen);
kp = (R_RSA_PUBLIC_KEY *)peer->pubkey.ptr;
j = i + 5 + temp / 4;
if (len < VALUE_LEN) {
rval = RV_LEN;
} else if (kp == NULL) {
rval = RV_PUB;
} else if (ntohl(pkt[j]) != kp->bits / 8) {
rval = RV_SIG;
} else if (tstamp == 0) {
rval = RV_TSP;
} else if (tstamp < ntohl(dhparam.fstamp) ||
fstamp < ntohl(dhparam.fstamp)) {
rval = RV_FSP;
} else if (fstamp == ntohl(dhparam.fstamp) &&
(peer->flags & FLAG_AUTOKEY)) {
peer->crypto &= ~CRYPTO_FLAG_DH;
rval = RV_FSP;
} else {
EVP_VerifyInit(&ctx, DA_MD5);
EVP_VerifyUpdate(&ctx, (u_char *)vp,
temp + 12);
rval = EVP_VerifyFinal(&ctx,
(u_char *)&pkt[j + 1],
ntohl(pkt[j]), kp);
}
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %x parameters %u ts %u fs %u\n",
rval, temp, tstamp, fstamp);
#endif
if (rval != RV_OK || temp == 0) {
if (rval != RV_TSP)
msyslog(LOG_ERR,
"crypto: %x parameters %u ts %u fs %u\n",
rval, temp, tstamp, fstamp);
break;
}
peer->flash &= ~TEST10;
crypto_flags |= CRYPTO_FLAG_DH;
peer->crypto &= ~CRYPTO_FLAG_DH;
dhparam.tstamp = vp->tstamp;
dhparam.fstamp = vp->fstamp;
dhparam.vallen = vp->vallen;
if (dhparam.ptr != NULL)
free(dhparam.ptr);
pp = emalloc(temp);
dhparam.ptr = (u_char *)pp;
memcpy(pp, vp->pkt, temp);
dh_params.primeLen = ntohl(*pp++);
dh_params.prime = (u_char *)pp;
pp += dh_params.primeLen / 4;
dh_params.generatorLen = ntohl(*pp++);
dh_params.generator = (u_char *)pp;
dh_keyLen = dh_params.primeLen / 2;
if (dh_private != NULL)
free(dh_private);
dh_private = emalloc(dh_keyLen);
if (dhparam.sig == NULL)
dhparam.sig = emalloc(private_key.bits /
8);
dhpub.tstamp = vp->tstamp;
dhpub.fstamp = vp->fstamp;
dhpub.vallen = htonl(dh_params.primeLen);
if (dhpub.ptr != NULL)
free(dhpub.ptr);
dhpub.ptr = emalloc(dh_params.primeLen);
if (dhpub.sig == NULL)
dhpub.sig = emalloc(private_key.bits /
8);
crypto_agree();
break;
case CRYPTO_DH:
peer->cmmd = ntohl(pkt[i]);
if (!crypto_flags)
peer->cmmd |= CRYPTO_ERROR;
case CRYPTO_DH | CRYPTO_RESP:
if (!crypto_flags)
break;
vp = (struct value *)&pkt[i + 2];
fstamp = ntohl(vp->fstamp);
temp = ntohl(vp->vallen);
kp = (R_RSA_PUBLIC_KEY *)peer->pubkey.ptr;
j = i + 5 + temp / 4;
if (len < VALUE_LEN) {
rval = RV_LEN;
} else if (temp != dh_params.primeLen) {
rval = RV_DH;
} else if (kp == NULL) {
rval = RV_PUB;
} else if (ntohl(pkt[j]) != kp->bits / 8) {
rval = RV_SIG;
} else if (tstamp == 0 || tstamp <
peer->pcookie.tstamp || (tstamp ==
peer->pcookie.tstamp && (peer->flags &
FLAG_AUTOKEY))) {
rval = RV_TSP;
} else {
EVP_VerifyInit(&ctx, DA_MD5);
EVP_VerifyUpdate(&ctx, (u_char *)vp,
temp + 12);
rval = EVP_VerifyFinal(&ctx,
(u_char *)&pkt[j + 1],
ntohl(pkt[j]), kp);
}
if (rval != RV_OK) {
temp = 0;
} else if (fstamp > dhparam.fstamp) {
crypto_flags &= ~CRYPTO_FLAG_DH;
rval = RV_FSP;
} else {
rval = R_ComputeDHAgreedKey(dh_key,
(u_char *)&pkt[i + 5], dh_private,
dh_keyLen, &dh_params);
temp = ntohl(*(u_int32 *)dh_key);
}
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %x agreement %08x ts %u (%u) fs %u\n",
rval, temp, tstamp,
peer->pcookie.tstamp, fstamp);
#endif
if (rval != RV_OK) {
if (rval != RV_TSP)
msyslog(LOG_ERR,
"crypto: %x agreement %08x ts %u (%u) fs %u\n",
rval, temp, tstamp,
peer->pcookie.tstamp,
fstamp);
peer->cmmd |= CRYPTO_ERROR;
break;
}
peer->flash &= ~TEST10;
peer->flags &= ~FLAG_AUTOKEY;
peer->assoc = ntohl(pkt[i + 1]);
peer->pcookie.tstamp = tstamp;
if (temp != peer->pcookie.key) {
peer->pcookie.key = temp;
key_expire(peer);
}
break;
case CRYPTO_TAI | CRYPTO_RESP:
if (!crypto_flags)
break;
vp = (struct value *)&pkt[i + 2];
fstamp = ntohl(vp->fstamp);
temp = ntohl(vp->vallen);
kp = (R_RSA_PUBLIC_KEY *)peer->pubkey.ptr;
j = i + 5 + temp / 4;
if (len < VALUE_LEN) {
rval = RV_LEN;
} if (kp == NULL) {
rval = RV_PUB;
} else if (ntohl(pkt[j]) != kp->bits / 8) {
rval = RV_SIG;
} else if (tstamp == 0) {
rval = RV_TSP;
} else if (tstamp < ntohl(tai_leap.fstamp) ||
fstamp < ntohl(tai_leap.fstamp)) {
rval = RV_FSP;
} else if (fstamp == ntohl(tai_leap.fstamp) &&
(peer->flags & FLAG_AUTOKEY)) {
peer->crypto &= ~CRYPTO_FLAG_TAI;
rval = RV_FSP;
} else {
EVP_VerifyInit(&ctx, DA_MD5);
EVP_VerifyUpdate(&ctx, (u_char *)vp,
temp + 12);
rval = EVP_VerifyFinal(&ctx,
(u_char *)&pkt[j + 1],
ntohl(pkt[j]), kp);
}
#ifdef DEBUG
if (debug)
printf(
"crypto_recv: verify %x leapseconds %u ts %u fs %u\n",
rval, temp, tstamp, fstamp);
#endif
if (rval != RV_OK || temp == 0) {
if (rval != RV_TSP)
msyslog(LOG_ERR,
"crypto: %x leapseconds %u ts %u fs %u\n",
rval, temp, tstamp, fstamp);
break;
}
peer->flash &= ~TEST10;
crypto_flags |= CRYPTO_FLAG_TAI;
peer->crypto &= ~CRYPTO_FLAG_TAI;
sys_tai = temp / 4 + TAI_1972 - 1;
#ifdef KERNEL_PLL
#if NTP_API > 3
ntv.modes = MOD_TAI;
ntv.constant = sys_tai;
(void)ntp_adjtime(&ntv);
#endif
#endif
tai_leap.tstamp = vp->tstamp;
tai_leap.fstamp = vp->fstamp;
tai_leap.vallen = vp->vallen;
if (tai_leap.ptr == NULL)
free(tai_leap.ptr);
tai_leap.ptr = emalloc(temp);
memcpy(tai_leap.ptr, vp->pkt, temp);
if (tai_leap.sig == NULL)
tai_leap.sig =
emalloc(private_key.bits / 8);
crypto_agree();
break;
#endif
default:
if (code & (CRYPTO_RESP | CRYPTO_ERROR))
break;
peer->cmmd = ntohl(pkt[i]);
break;
}
authlen += len;
}
}
int
crypto_xmit(
u_int32 *xpkt,
int start,
u_int code,
keyid_t cookie,
u_int associd
)
{
struct peer *peer;
struct autokey *ap;
struct cookie *cp;
int len;
u_int opcode;
int i;
#ifdef PUBKEY
R_SIGNATURE_CTX ctx;
struct value *vp;
int rval;
u_int temp;
int j;
#endif
i = start / 4;
opcode = code;
xpkt[i + 1] = htonl(associd);
len = 8;
switch (opcode) {
case CRYPTO_ASSOC | CRYPTO_RESP:
cp = (struct cookie *)&xpkt[i + 2];
#ifdef PUBKEY
cp->tstamp = host.tstamp;
#else
cp->tstamp = 0;
#endif
cp->key = htonl(crypto_flags);
cp->siglen = 0;
len += 12;
break;
case CRYPTO_AUTO | CRYPTO_RESP:
peer = findpeerbyassoc(associd);
if (peer == NULL) {
opcode |= CRYPTO_ERROR;
break;
}
peer->flags &= ~FLAG_ASSOC;
ap = (struct autokey *)&xpkt[i + 2];
ap->tstamp = peer->sndauto.tstamp;
ap->seq = peer->sndauto.seq;
ap->key = peer->sndauto.key;
ap->siglen = peer->sndauto.siglen;
len += 16;
#ifdef PUBKEY
if (!crypto_flags)
break;
temp = ntohl(ap->siglen);
if (temp != 0)
memcpy(ap->pkt, peer->sndauto.sig, temp);
len += temp;
#endif
break;
case CRYPTO_PRIV:
case CRYPTO_PRIV | CRYPTO_RESP:
cp = (struct cookie *)&xpkt[i + 2];
cp->key = htonl(cookie);
cp->siglen = 0;
len += 12;
#ifdef PUBKEY
cp->tstamp = host.tstamp;
if (!crypto_flags)
break;
EVP_SignInit(&ctx, DA_MD5);
EVP_SignUpdate(&ctx, (u_char *)cp, 8);
rval = EVP_SignFinal(&ctx, (u_char *)cp->pkt, &temp,
&private_key);
if (rval != RV_OK) {
msyslog(LOG_ERR,
"crypto: cookie signature fails %x", rval);
break;
}
cp->siglen = htonl(temp);
len += temp;
#endif
break;
#ifdef PUBKEY
case CRYPTO_CERT | CRYPTO_RESP:
if (!crypto_flags) {
opcode |= CRYPTO_ERROR;
break;
}
vp = (struct value *)&xpkt[i + 2];
vp->tstamp = certif.tstamp;
vp->fstamp = certif.fstamp;
vp->vallen = 0;
len += 12;
temp = ntohl(certif.vallen);
if (temp == 0)
break;
vp->vallen = htonl(temp);
memcpy(vp->pkt, certif.ptr, temp);
len += temp;
j = i + 5 + temp / 4;
temp = public_key.bits / 8;
xpkt[j++] = htonl(temp);
memcpy(&xpkt[j], certif.sig, temp);
len += temp + 4;
break;
case CRYPTO_DHPAR | CRYPTO_RESP:
if (!crypto_flags) {
opcode |= CRYPTO_ERROR;
break;
}
vp = (struct value *)&xpkt[i + 2];
vp->tstamp = dhparam.tstamp;
vp->fstamp = dhparam.fstamp;
vp->vallen = 0;
len += 12;
temp = ntohl(dhparam.vallen);
if (temp == 0)
break;
vp->vallen = htonl(temp);
memcpy(vp->pkt, dhparam.ptr, temp);
len += temp;
j = i + 5 + temp / 4;
temp = public_key.bits / 8;
xpkt[j++] = htonl(temp);
memcpy(&xpkt[j], dhparam.sig, temp);
len += temp + 4;
break;
case CRYPTO_DH:
case CRYPTO_DH | CRYPTO_RESP:
if (!crypto_flags) {
opcode |= CRYPTO_ERROR;
break;
}
vp = (struct value *)&xpkt[i + 2];
vp->tstamp = dhpub.tstamp;
vp->fstamp = dhpub.fstamp;
vp->vallen = 0;
len += 12;
temp = ntohl(dhpub.vallen);
if (temp == 0)
break;
vp->vallen = htonl(temp);
memcpy(vp->pkt, dhpub.ptr, temp);
len += temp;
j = i + 5 + temp / 4;
temp = public_key.bits / 8;
xpkt[j++] = htonl(temp);
memcpy(&xpkt[j], dhpub.sig, temp);
len += temp + 4;
break;
case CRYPTO_NAME | CRYPTO_RESP:
if (!crypto_flags) {
opcode |= CRYPTO_ERROR;
break;
}
vp = (struct value *)&xpkt[i + 2];
vp->tstamp = host.tstamp;
vp->fstamp = host.fstamp;
vp->vallen = 0;
len += 12;
temp = ntohl(host.vallen);
if (temp == 0)
break;
vp->vallen = htonl(temp);
memcpy(vp->pkt, host.ptr, temp);
len += temp;
j = i + 5 + temp / 4;
temp = public_key.bits / 8;
xpkt[j++] = htonl(temp);
memcpy(&xpkt[j], host.sig, temp);
len += temp + 4;
break;
case CRYPTO_TAI | CRYPTO_RESP:
if (!crypto_flags) {
opcode |= CRYPTO_ERROR;
break;
}
vp = (struct value *)&xpkt[i + 2];
vp->tstamp = tai_leap.tstamp;
vp->fstamp = tai_leap.fstamp;
vp->vallen = 0;
len += 12;
temp = ntohl(tai_leap.vallen);
if (temp == 0)
break;
vp->vallen = htonl(temp);
memcpy(vp->pkt, tai_leap.ptr, temp);
len += temp;
j = i + 5 + temp / 4;
temp = public_key.bits / 8;
xpkt[j++] = htonl(temp);
memcpy(&xpkt[j], tai_leap.sig, temp);
len += temp + 4;
break;
#endif
default:
if (opcode & CRYPTO_RESP)
opcode |= CRYPTO_ERROR;
break;
}
len = ((len + 7) / 8) * 8;
if (len >= 4) {
xpkt[i] = htonl((u_int32)((opcode << 16) | len));
#ifdef DEBUG
if (debug)
printf(
"crypto_xmit: ext offset %d len %d code %x assocID %d\n",
start, len, code, associd);
#endif
}
return (len);
}
#ifdef PUBKEY
void
crypto_setup(void)
{
char filename[MAXFILENAME];
u_int fstamp;
u_int len, temp;
u_int32 *pp;
memset(&private_key, 0, sizeof(private_key));
memset(&public_key, 0, sizeof(public_key));
memset(&certif, 0, sizeof(certif));
memset(&dh_params, 0, sizeof(dh_params));
memset(&host, 0, sizeof(host));
memset(&dhparam, 0, sizeof(dhparam));
memset(&dhpub, 0, sizeof(dhpub));
memset(&tai_leap, 0, sizeof(tai_leap));
if (!crypto_flags)
return;
if (private_key_file == NULL)
private_key_file = "ntpkey";
host.fstamp = htonl(crypto_rsa(private_key_file,
(u_char *)&private_key, sizeof(R_RSA_PRIVATE_KEY)));
if (public_key_file == NULL) {
snprintf(filename, MAXFILENAME, "ntpkey_%s",
sys_hostname);
public_key_file = emalloc(strlen(filename) + 1);
strcpy(public_key_file, filename);
}
fstamp = htonl(crypto_rsa(public_key_file,
(u_char *)&public_key, sizeof(R_RSA_PUBLIC_KEY)));
if (fstamp != host.fstamp || strstr(public_key_file,
sys_hostname) == NULL) {
msyslog(LOG_ERR,
"crypto: public/private key files mismatch");
exit (-1);
}
crypto_flags |= CRYPTO_FLAG_RSA;
strcpy(filename, sys_hostname);
for (len = strlen(filename) + 1; len % 4 != 0; len++)
filename[len - 1] = 0;
temp = sizeof(R_RSA_PUBLIC_KEY) - sizeof(u_int) + 4;
host.vallen = htonl(temp + len);
pp = emalloc(temp + len);
host.ptr = (u_char *)pp;
*pp++ = htonl(public_key.bits);
memcpy(pp--, public_key.modulus, temp - 4);
pp += temp / 4;
memcpy(pp, filename, len);
host.sig = emalloc(private_key.bits / 8);
if (certif_file == NULL)
snprintf(filename, MAXFILENAME, "ntpkey_certif_%s",
sys_hostname);
certif_file = emalloc(strlen(filename) + 1);
strcpy(certif_file, filename);
crypto_cert(certif_file);
if (dh_params_file == NULL)
dh_params_file = "ntpkey_dh";
crypto_dh(dh_params_file);
if (tai_leap_file == NULL)
tai_leap_file = "ntpkey_leap";
crypto_tai(tai_leap_file);
}
void
crypto_agree(void)
{
R_RANDOM_STRUCT randomstr;
R_SIGNATURE_CTX ctx;
l_fp lstamp;
tstamp_t tstamp;
u_int len, temp;
int rval, i;
if (sys_leap == LEAP_NOTINSYNC)
return;
get_systime(&lstamp);
tstamp = lstamp.l_ui;
host.tstamp = htonl(tstamp);
if (!crypto_flags)
return;
EVP_SignInit(&ctx, DA_MD5);
EVP_SignUpdate(&ctx, (u_char *)&host, 12);
EVP_SignUpdate(&ctx, host.ptr, ntohl(host.vallen));
rval = EVP_SignFinal(&ctx, host.sig, &len, &private_key);
if (rval != RV_OK || len != private_key.bits / 8) {
msyslog(LOG_ERR, "crypto: host signature fails %x",
rval);
exit (-1);
}
host.siglen = ntohl(len);
if (certif.vallen != 0) {
certif.tstamp = htonl(tstamp);
EVP_SignInit(&ctx, DA_MD5);
EVP_SignUpdate(&ctx, (u_char *)&certif, 12);
EVP_SignUpdate(&ctx, certif.ptr,
ntohl(certif.vallen));
rval = EVP_SignFinal(&ctx, certif.sig, &len,
&private_key);
if (rval != RV_OK || len != private_key.bits / 8) {
msyslog(LOG_ERR,
"crypto: certificate signature fails %x",
rval);
exit (-1);
}
certif.siglen = ntohl(len);
}
if (dhparam.vallen != 0) {
dhparam.tstamp = htonl(tstamp);
EVP_SignInit(&ctx, DA_MD5);
EVP_SignUpdate(&ctx, (u_char *)&dhparam, 12);
EVP_SignUpdate(&ctx, dhparam.ptr,
ntohl(dhparam.vallen));
rval = EVP_SignFinal(&ctx, dhparam.sig, &len,
&private_key);
if (rval != RV_OK || len != private_key.bits / 8) {
msyslog(LOG_ERR,
"crypto: parameters signature fails %x",
rval);
exit (-11);
}
dhparam.siglen = ntohl(len);
R_RandomInit(&randomstr);
R_GetRandomBytesNeeded(&len, &randomstr);
for (i = 0; i < len; i++) {
temp = RANDOM;
R_RandomUpdate(&randomstr, (u_char *)&temp, 1);
}
rval = R_SetupDHAgreement(dhpub.ptr, dh_private,
dh_keyLen, &dh_params, &randomstr);
if (rval != RV_OK) {
msyslog(LOG_ERR,
"crypto: invalid public value");
exit (-1);
}
dhpub.tstamp = htonl(tstamp);
EVP_SignInit(&ctx, DA_MD5);
EVP_SignUpdate(&ctx, (u_char *)&dhpub, 12);
EVP_SignUpdate(&ctx, dhpub.ptr, ntohl(dhpub.vallen));
rval = EVP_SignFinal(&ctx, dhpub.sig, &len,
&private_key);
if (rval != RV_OK || len != private_key.bits / 8) {
msyslog(LOG_ERR,
"crypto: public value signature fails %x",
rval);
exit (-1);
}
dhpub.siglen = ntohl(len);
}
if (tai_leap.vallen != 0) {
tai_leap.tstamp = htonl(tstamp);
EVP_SignInit(&ctx, DA_MD5);
EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12);
EVP_SignUpdate(&ctx, tai_leap.ptr,
ntohl(tai_leap.vallen));
rval = EVP_SignFinal(&ctx, tai_leap.sig, &len,
&private_key);
if (rval != RV_OK || len != private_key.bits / 8) {
msyslog(LOG_ERR,
"crypto: leapseconds signature fails %x",
rval);
exit (-1);
}
tai_leap.siglen = ntohl(len);
}
#ifdef DEBUG
if (debug)
printf(
"cypto_agree: ts %u host %u par %u pub %u leap %u\n",
tstamp, ntohl(host.fstamp), ntohl(dhparam.fstamp),
ntohl(dhpub.fstamp), ntohl(tai_leap.fstamp));
#endif
}
static u_int
crypto_rsa(
char *cp,
u_char *key,
u_int keylen
)
{
FILE *str;
u_char buf[MAX_LINLEN];
u_char encoded_key[MAX_ENCLEN];
char filename[MAXFILENAME];
char linkname[MAXFILENAME];
u_int fstamp;
u_int bits, len;
char *rptr;
int rval;
if (*cp == '/')
strcpy(filename, cp);
else
snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
str = fopen(filename, "r");
if (str == NULL) {
msyslog(LOG_ERR, "crypto: RSA file %s not found",
filename);
exit (-1);
}
while ((rptr = fgets(buf, MAX_LINLEN - 1, str)) != NULL) {
len = strlen(buf);
if (len < 1)
continue;
if (*buf == '#' || *buf == '\r' || *buf == '\0')
continue;
break;
}
if (rptr == NULL)
rval = RV_DAT;
else if (sscanf(buf, "%d %s", &bits, encoded_key) != 2)
rval = RV_DAT;
else if (R_DecodePEMBlock(&buf[sizeof(u_int)], &len,
encoded_key, strlen(encoded_key)))
rval = RV_DEC;
else if ((len += sizeof(u_int)) != keylen)
rval = RV_KEY;
else if (bits < MIN_RSA_MODULUS_BITS || bits >
MAX_RSA_MODULUS_BITS)
rval = RV_KEY;
else
rval = RV_OK;
if (rval != RV_OK) {
fclose(str);
msyslog(LOG_ERR, "crypto: RSA file %s error %x", cp,
rval);
exit (-1);
}
fclose(str);
*(u_int *)buf = bits;
memcpy(key, buf, keylen);
rval = readlink(filename, linkname, MAXFILENAME - 1);
if (rval > 0) {
linkname[rval] = '\0';
rptr = strrchr(linkname, '.');
} else {
rptr = strrchr(filename, '.');
}
if (rptr != NULL)
sscanf(++rptr, "%u", &fstamp);
else
fstamp = 0;
#ifdef DEBUG
if (debug)
printf(
"crypto_rsa: key file %s link %d fs %u modulus %d\n",
cp, rval, fstamp, bits);
#endif
return (fstamp);
}
static void
crypto_cert(
char *cp
)
{
u_char buf[5000];
char filename[MAXFILENAME];
char linkname[MAXFILENAME];
u_int fstamp;
u_int32 *pp;
u_int len;
char *rptr;
int rval, fd;
if (*cp == '/')
strcpy(filename, cp);
else
snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
fd = open(filename, O_RDONLY, 0777);
if (fd <= 0) {
msyslog(LOG_INFO,
"crypto: certificate file %s not found",
filename);
return;
}
rval = RV_OK;
len = read(fd, buf, 5000);
close(fd);
if (rval != RV_OK) {
msyslog(LOG_ERR,
"crypto: certificate file %s error %d", cp,
rval);
exit (-1);
}
certif.vallen = htonl(200);
pp = emalloc(len);
certif.ptr = (u_char *)pp;
memcpy(pp, buf, len);
certif.sig = emalloc(private_key.bits / 8);
crypto_flags |= CRYPTO_FLAG_CERT;
rval = readlink(filename, linkname, MAXFILENAME - 1);
if (rval > 0) {
linkname[rval] = '\0';
rptr = strrchr(linkname, '.');
} else {
rptr = strrchr(filename, '.');
}
if (rptr != NULL)
sscanf(++rptr, "%u", &fstamp);
else
fstamp = 0;
certif.fstamp = htonl(fstamp);
#ifdef DEBUG
if (debug)
printf(
"crypto_cert: certif file %s link %d fs %u len %d\n",
cp, rval, fstamp, len);
#endif
}
static void
crypto_dh(
char *cp
)
{
FILE *str;
u_char buf[MAX_LINLEN];
u_char encoded_key[MAX_ENCLEN];
u_char prime[MAX_KEYLEN];
u_char generator[MAX_KEYLEN];
u_int primelen;
u_int generatorlen;
char filename[MAXFILENAME];
char linkname[MAXFILENAME];
u_int fstamp;
u_int32 *pp;
u_int len;
char *rptr;
int rval;
if (*cp == '/')
strcpy(filename, cp);
else
snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
str = fopen(filename, "r");
if (str == NULL) {
msyslog(LOG_INFO,
"crypto: parameters file %s not found", filename);
return;
}
while ((rptr = fgets(buf, MAX_LINLEN - 1, str)) != NULL) {
if (strlen(buf) < 1)
continue;
if (*buf == '#' || *buf == '\r' || *buf == '\0')
continue;
break;
}
if (rptr == NULL)
rval = RV_DAT;
else if (sscanf(buf, "%u %s", &primelen, encoded_key) != 2)
rval = RV_DAT;
else if (primelen > MAX_KEYLEN)
rval = RV_KEY;
else if (R_DecodePEMBlock(prime, &len, encoded_key,
strlen(encoded_key)))
rval = RV_DEC;
else if (primelen != len || primelen >
DECODED_CONTENT_LEN(strlen(encoded_key)))
rval = RV_DAT;
else if (fscanf(str, "%u %s", &generatorlen, encoded_key) != 2)
rval = RV_DAT;
else if (generatorlen > MAX_KEYLEN)
rval = RV_KEY;
else if (R_DecodePEMBlock(generator, &len, encoded_key,
strlen(encoded_key)))
rval = RV_DEC;
else if (generatorlen != len || generatorlen >
DECODED_CONTENT_LEN(strlen(encoded_key)))
rval = RV_DAT;
else
rval = RV_OK;
if (rval != RV_OK) {
msyslog(LOG_ERR,
"crypto: parameters file %s error %x", cp,
rval);
exit (-1);
}
fclose(str);
len = 4 + primelen + 4 + generatorlen;
dhparam.vallen = htonl(len);
pp = emalloc(len);
dhparam.ptr = (u_char *)pp;
*pp++ = htonl(primelen);
memcpy(pp, prime, primelen);
dh_params.prime = (u_char *)pp;
pp += primelen / 4;
*pp++ = htonl(generatorlen);
memcpy(pp, &generator, generatorlen);
dh_params.generator = (u_char *)pp;
dh_params.primeLen = primelen;
dh_params.generatorLen = generatorlen;
dh_keyLen = primelen / 2;
dh_private = emalloc(dh_keyLen);
dhparam.sig = emalloc(private_key.bits / 8);
crypto_flags |= CRYPTO_FLAG_DH;
dhpub.vallen = htonl(dh_params.primeLen);
dhpub.ptr = emalloc(dh_params.primeLen);
dhpub.sig = emalloc(private_key.bits / 8);
rval = readlink(filename, linkname, MAXFILENAME - 1);
if (rval > 0) {
linkname[rval] = '\0';
rptr = strrchr(linkname, '.');
} else {
rptr = strrchr(filename, '.');
}
if (rptr != NULL)
sscanf(++rptr, "%u", &fstamp);
else
fstamp = 0;
dhparam.fstamp = htonl(fstamp);
dhpub.fstamp = htonl(fstamp);
#ifdef DEBUG
if (debug)
printf(
"crypto_dh: pars file %s link %d fs %u prime %u gen %u\n",
cp, rval, fstamp, dh_params.primeLen,
dh_params.generatorLen);
#endif
}
static void
crypto_tai(
char *cp
)
{
FILE *str;
u_char buf[MAX_LINLEN];
u_int leapsec[MAX_LEAP];
u_int offset;
char filename[MAXFILENAME];
char linkname[MAXFILENAME];
u_int fstamp;
u_int32 *pp;
u_int len;
char *rptr;
int rval, i;
#ifdef KERNEL_PLL
#if NTP_API > 3
struct timex ntv;
#endif
#endif
if (*cp == '/')
strcpy(filename, cp);
else
snprintf(filename, MAXFILENAME, "%s/%s", keysdir, cp);
str = fopen(filename, "r");
if (str == NULL) {
msyslog(LOG_INFO,
"crypto: leapseconds file %s not found",
filename);
return;
}
i = 0;
rval = RV_OK;
while (i < MAX_LEAP) {
rptr = fgets(buf, MAX_LINLEN - 1, str);
if (rptr == NULL)
break;
if (strlen(buf) < 1)
continue;
if (*buf == '#')
continue;
if (sscanf(buf, "%u %u", &leapsec[i], &offset) != 2)
continue;
if (i != offset - TAI_1972) {
rval = RV_DAT;
break;
}
i++;
}
fclose(str);
if (rval != RV_OK || i == 0) {
msyslog(LOG_ERR,
"crypto: leapseconds file %s error %d", cp,
rval);
exit (-1);
}
len = i * 4;
tai_leap.vallen = htonl(len);
pp = emalloc(len);
tai_leap.ptr = (u_char *)pp;
for (; i >= 0; i--) {
*pp++ = htonl(leapsec[i]);
}
tai_leap.sig = emalloc(private_key.bits / 8);
crypto_flags |= CRYPTO_FLAG_TAI;
sys_tai = len / 4 + TAI_1972 - 1;
#ifdef KERNEL_PLL
#if NTP_API > 3
ntv.modes = MOD_TAI;
ntv.constant = sys_tai;
if (ntp_adjtime(&ntv) == TIME_ERROR)
msyslog(LOG_ERR,
"crypto: kernel TAI update failed");
#endif
#endif
rval = readlink(filename, linkname, MAXFILENAME - 1);
if (rval > 0) {
linkname[rval] = '\0';
rptr = strrchr(linkname, '.');
} else {
rptr = strrchr(filename, '.');
}
if (rptr != NULL)
sscanf(++rptr, "%u", &fstamp);
else
fstamp = 0;
tai_leap.fstamp = htonl(fstamp);
#ifdef DEBUG
if (debug)
printf(
"crypto_tai: leapseconds file %s link %d fs %u offset %u\n",
cp, rval, fstamp, ntohl(tai_leap.vallen) / 4 +
TAI_1972);
#endif
}
void
crypto_config(
int item,
char *cp
)
{
switch (item) {
case CRYPTO_CONF_FLAGS:
sscanf(cp, "%x", &crypto_flags);
break;
case CRYPTO_CONF_PRIV:
private_key_file = emalloc(strlen(cp) + 1);
strcpy(private_key_file, cp);
break;
case CRYPTO_CONF_PUBL:
public_key_file = emalloc(strlen(cp) + 1);
strcpy(public_key_file, cp);
break;
case CRYPTO_CONF_CERT:
certif_file = emalloc(strlen(cp) + 1);
strcpy(certif_file, cp);
break;
case CRYPTO_CONF_DH:
dh_params_file = emalloc(strlen(cp) + 1);
strcpy(dh_params_file, cp);
break;
case CRYPTO_CONF_LEAP:
tai_leap_file = emalloc(strlen(cp) + 1);
strcpy(tai_leap_file, cp);
break;
case CRYPTO_CONF_KEYS:
keysdir = emalloc(strlen(cp) + 1);
strcpy(keysdir, cp);
break;
}
crypto_flags |= CRYPTO_FLAG_ENAB;
}
# else
int ntp_crypto_bs_pubkey;
# endif
#else
int ntp_crypto_bs_autokey;
#endif