freebsd-nq/contrib/ntp/ntpd/ntp_crypto.c

2059 lines
53 KiB
C
Raw Normal View History

2001-08-29 14:35:15 +00:00
/*
* ntp_crypto.c - NTP version 4 public key routines
*/
#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 /* KERNEL_PLL */
/*
* Extension field message formats
*
* +-------+-------+ +-------+-------+ +-------+-------+
* 0 | 3 | len | | 2,4 | len | | 5-9 | len |
* +-------+-------+ +-------+-------+ +-------+-------+
* 1 | assocID | | assocID | | assocID |
* +---------------+ +---------------+ +---------------+
* 2 | timestamp | | timestamp | | timestamp |
* +---------------+ +---------------+ +---------------+
* 3 | final seq | | cookie/flags | | filestamp |
* +---------------+ +---------------+ +---------------+
* 4 | final key | | signature len | | value len |
* +---------------+ +---------------+ +---------------+
* 5 | signature len | | | | |
* +---------------+ = signature = = value =
* 6 | | | | | |
* = signature = +---------------+ +---------------+
* 7 | | CRYPTO_ASSOC rsp | signature len |
* +---------------+ CRYPTO_PRIV rsp +---------------+
* CRYPTO_AUTO rsp | |
* = signature =
* | |
* +---------------+
* CRYPTO_DHPAR rsp
* CRYPTO_DH rsp
* CRYPTO_NAME rsp
* CRYPTO_CERT rsp
* CRYPTO_TAI rsp
*
* CRYPTO_STAT 1 - offer/select
* CRYPTO_ASSOC 2 20 association ID
* CRYPTO_AUTO 3 88 autokey values
* CRYPTO_PRIV 4 84 cookie value
* CRYPTO_DHPAR 5 220 agreement parameters
* CRYPTO_DH 6 152 public value
* CRYPTO_NAME 7 460 host name/public key
* CRYPTO_CERT 8 ? certificate
* CRYPTO_TAI 9 144 leapseconds table
*
* Note: requests carry the association ID of the receiver; responses
* carry the association ID of the sender.
*/
/*
* Minimum sizes of fields
*/
#define COOKIE_LEN (5 * 4)
#define AUTOKEY_LEN (6 * 4)
#define VALUE_LEN (6 * 4)
/*
* Global cryptodata in host byte order.
*/
u_int crypto_flags; /* status word */
u_int sys_tai; /* current UTC offset from TAI */
#ifdef PUBKEY
/*
* Cryptodefines
*/
#define TAI_1972 10 /* initial TAI offset */
#define MAX_LEAP 100 /* max UTC leapseconds */
#define MAX_LINLEN 1024 /* max line */
#define MAX_KEYLEN 1024 /* max key */
#define MAX_ENCLEN (ENCODED_CONTENT_LEN(1024)) /* max enc key */
/*
* Private cryptodata in network byte order.
*/
static R_RSA_PRIVATE_KEY private_key; /* private key */
static R_RSA_PUBLIC_KEY public_key; /* public key */
static R_DH_PARAMS dh_params; /* agreement parameters */
static u_char *dh_private; /* private value */
static u_int dh_keyLen; /* private value length */
static char *keysdir = NTP_KEYSDIR; /* crypto keys directory */
static char *private_key_file = NULL; /* private key file */
static char *public_key_file = NULL; /* public key file */
static char *certif_file = NULL; /* certificate file */
static char *dh_params_file = NULL; /* agreement parameters file */
static char *tai_leap_file = NULL; /* leapseconds file */
/*
* Global cryptodata in network byte order
*/
struct value host; /* host name/public key */
struct value certif; /* certificate */
struct value dhparam; /* agreement parameters */
struct value dhpub; /* public value */
struct value tai_leap; /* leapseconds table */
/*
* Cryptotypes
*/
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 /* PUBKEY */
/*
* Autokey protocol status codes
*/
#define RV_OK 0 /* success */
#define RV_LEN 1 /* invalid field length */
#define RV_TSP 2 /* invalid timestamp */
#define RV_FSP 3 /* invalid filestamp */
#define RV_PUB 4 /* missing public key */
#define RV_KEY 5 /* invalid RSA modulus */
#define RV_SIG 6 /* invalid signature length */
#define RV_DH 7 /* invalid agreement parameters */
#define RV_FIL 8 /* missing or corrupted key file */
#define RV_DAT 9 /* missing or corrupted data */
#define RV_DEC 10 /* PEM decoding error */
#define RV_DUP 11 /* duplicate flags */
#define RV_VN 12 /* incorrect version */
/*
* session_key - generate session key
*
* This routine generates a session key from the source address,
* destination address, key ID and private value. The value of the
* session key is the MD5 hash of these values, while the next key ID is
* the first four octets of the hash.
*/
keyid_t /* returns next key ID */
session_key(
struct sockaddr_in *srcadr, /* source address */
struct sockaddr_in *dstadr, /* destination address */
keyid_t keyno, /* key ID */
keyid_t private, /* private value */
u_long lifetime /* key lifetime */
)
{
MD5_CTX ctx; /* MD5 context */
keyid_t keyid; /* key identifer */
u_int32 header[4]; /* data in network byte order */
u_char digest[16]; /* message digest */
/*
* Generate the session key and key ID. If the lifetime is
* greater than zero, install the key and call it trusted.
*/
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);
}
/*
* make_keylist - generate key list
*
* This routine constructs a pseudo-random sequence by repeatedly
* hashing the session key starting from a given source address,
* destination address, private value and the next key ID of the
* preceeding session key. The last entry on the list is saved along
* with its sequence number and public signature.
*/
void
make_keylist(
struct peer *peer, /* peer structure pointer */
struct interface *dstadr /* interface */
)
{
struct autokey *ap; /* autokey pointer */
keyid_t keyid; /* next key ID */
keyid_t cookie; /* private value */
l_fp tstamp; /* NTP timestamp */
u_long ltemp;
int i;
#ifdef PUBKEY
R_SIGNATURE_CTX ctx; /* signature context */
int rval; /* return value */
u_int len;
#endif /* PUBKEY */
/*
* Allocate the key list if necessary.
*/
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);
/*
* Generate an initial key ID which is unique and greater than
* NTP_MAXKEY.
*/
while (1) {
keyid = (u_long)RANDOM & 0xffffffff;
if (keyid <= NTP_MAXKEY)
continue;
if (authhavekey(keyid))
continue;
break;
}
/*
* Generate up to NTP_MAXSESSION session keys. Stop if the
* next one would not be unique or not a session key ID or if
* it would expire before the next poll. The private value
* included in the hash is zero if broadcast mode, the peer
* cookie if client mode or the host cookie if symmetric modes.
*/
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;
}
/*
* Save the last session key ID, sequence number and timestamp,
* then sign these values for later retrieval by the clients. Be
* careful not to use invalid key media.
*/
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 /* PUBKEY */
}
/*
* crypto_recv - parse extension fields
*
* This routine is called when the packet has been matched to an
* association and passed sanity, format and MAC checks. We believe the
* extension field values only if the field has proper format and
* length, the timestamp and filestamp are valid and the signature has
* valid length and is verified. There are a few cases where some values
* are believed even if the signature fails, but only if the authentic
* bit is not set.
*/
void
crypto_recv(
struct peer *peer, /* peer structure pointer */
struct recvbuf *rbufp /* packet buffer pointer */
)
{
u_int32 *pkt; /* packet pointer */
struct autokey *ap; /* autokey pointer */
struct cookie *cp; /* cookie pointer */
int has_mac; /* length of MAC field */
int authlen; /* offset of MAC field */
int len; /* extension field length */
u_int code; /* extension field opcode */
tstamp_t tstamp; /* timestamp */
int i, rval;
u_int temp;
#ifdef PUBKEY
R_SIGNATURE_CTX ctx; /* signature context */
struct value *vp; /* value pointer */
u_char dh_key[MAX_KEYLEN]; /* agreed key */
R_RSA_PUBLIC_KEY *kp; /* temporary public key pointer */
tstamp_t fstamp; /* filestamp */
u_int32 *pp; /* packet pointer */
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; /* kernel interface structure */
#endif /* NTP_API */
#endif /* KERNEL_PLL */
#endif /* PUBKEY */
/*
* Initialize. Note that the packet has already been checked for
* valid format and extension field lengths. We first extract
* the field length, command code and timestamp in host byte
* order. These are used with all commands and modes. We discard
* old timestamps and filestamps; but, for duplicate timestamps
* we discard only if the authentic bit is set. Cute.
*/
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) {
/*
* Install association ID and status word.
*/
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;
/*
* Install autokey values in broadcast client and
* symmetric modes.
*/
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 /* PUBKEY */
if (tstamp < peer->recauto.tstamp || (tstamp ==
peer->recauto.tstamp && (peer->flags &
FLAG_AUTOKEY)))
rval = RV_TSP;
else
rval = RV_OK;
#endif /* PUBKEY */
#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;
/*
* Install session cookie in client mode. Use this also
* in symmetric modes for test when rsaref20 has not
* been installed.
*/
case CRYPTO_PRIV:
peer->cmmd = ntohl(pkt[i]);
/* fall through */
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 /* PUBKEY */
if (tstamp <= peer->pcookie.tstamp || (tstamp ==
peer->pcookie.tstamp && (peer->flags &
FLAG_AUTOKEY)))
rval = RV_TSP;
else
rval = RV_OK;
#endif /* PUBKEY */
/*
* Tricky here. If in client mode, use the
* server cookie; otherwise, use EXOR of both
* peer cookies. We call this Daffy-Hooligan
* agreement.
*/
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;
/*
* The following commands and responses work only when
* public-key cryptography has been configured. If
* configured, but disabled due to no crypto command in
* the configuration file, they are ignored.
*/
#ifdef PUBKEY
/*
* Install public key and host name.
*/
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;
/*
* Install certificate.
*/
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 the peer data are newer than the host
* data, replace the host data. Otherwise,
* wait for the peer to fetch the host data.
*/
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;
/*
* Initialize agreement parameters and extension
* field in network byte order. Note the private
* key length is set arbitrarily at half the
* prime length.
*/
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;
/*
* Install agreement parameters in symmetric modes.
*/
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 the peer data are newer than the host
* data, replace the host data. Otherwise,
* wait for the peer to fetch the host data.
*/
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;
/*
* Initialize agreement parameters and extension
* field in network byte order. Note the private
* key length is set arbitrarily at half the
* prime length.
*/
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);
/*
* Initialize public value extension field.
*/
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;
/*
* Verify public value and compute agreed key in
* symmetric modes.
*/
case CRYPTO_DH:
peer->cmmd = ntohl(pkt[i]);
if (!crypto_flags)
peer->cmmd |= CRYPTO_ERROR;
/* fall through */
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);
}
/*
* Run the agreement algorithm and stash the key
* value. We use only the first u_int32 for the
* host cookie. Wasteful. If the association ID
* is zero, the other guy hasn't seen us as
* synchronized, in which case both of us should
* be using a zero cookie.
*/
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;
/*
* Install leapseconds table.
*/
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 the peer data are newer than the host
* data, replace the host data. Otherwise,
* wait for the peer to fetch the host data.
*/
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;
2002-10-29 19:58:12 +00:00
(void)ntp_adjtime(&ntv);
2001-08-29 14:35:15 +00:00
#endif /* NTP_API */
#endif /* KERNEL_PLL */
/*
* Initialize leapseconds table and extension
* field in network byte order.
*/
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 /* PUBKEY */
/*
* For other requests, save the request code for later;
* for unknown responses or errors, just ignore for now.
*/
default:
if (code & (CRYPTO_RESP | CRYPTO_ERROR))
break;
peer->cmmd = ntohl(pkt[i]);
break;
}
authlen += len;
}
}
/*
* crypto_xmit - construct extension fields
*
* This routine is called both when an association is configured and
* when one is not. The only case where this matters now is to retrieve
* the autokey information, in which case the caller has to provide the
* association ID to match the association.
*/
int /* return length of extension field */
crypto_xmit(
u_int32 *xpkt, /* packet pointer */
int start, /* offset to extension field */
u_int code, /* extension field code */
keyid_t cookie, /* session cookie */
u_int associd /* association ID */
)
{
struct peer *peer; /* peer structure pointer */
struct autokey *ap; /* autokey pointer */
struct cookie *cp; /* cookie pointer */
int len; /* extension field length */
u_int opcode; /* extension field opcode */
int i;
#ifdef PUBKEY
R_SIGNATURE_CTX ctx; /* signature context */
struct value *vp; /* value pointer */
int rval; /* return value */
u_int temp;
int j;
#endif /* PUBKEY */
/*
* Generate the requested extension field request code, length
* and association ID. Note that several extension fields are
* used with and without public-key cryptography. If public-key
* cryptography has not been configured, we do the same thing,
* but leave off the signature.
*/
i = start / 4;
opcode = code;
xpkt[i + 1] = htonl(associd);
len = 8;
switch (opcode) {
/*
* Send association ID, timestamp and status word.
*/
case CRYPTO_ASSOC | CRYPTO_RESP:
cp = (struct cookie *)&xpkt[i + 2];
#ifdef PUBKEY
cp->tstamp = host.tstamp;
#else
cp->tstamp = 0;
#endif /* PUBKEY */
cp->key = htonl(crypto_flags);
cp->siglen = 0;
len += 12;
break;
/*
* Find peer and send autokey data and signature in broadcast
* server and symmetric modes. If no association is found,
* either the server has restarted with new associations or some
* perp has replayed an old message.
*/
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 /* PUBKEY */
break;
/*
* Send peer cookie and signature in server mode.
*/
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 /* PUBKEY */
break;
#ifdef PUBKEY
/*
* The following commands and responses work only when public-
* key cryptography has been configured. If configured, but
* disabled due to no crypto command in the configuration file,
* they are ignored and an error response is returned.
*/
/*
* Send certificate, timestamp and signature.
*/
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;
/*
* Send agreement parameters, timestamp and signature.
*/
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;
/*
* Send public value, timestamp and signature.
*/
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;
/*
* Send public key, host name, timestamp and signature.
*/
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;
/*
* Send leapseconds table, timestamp and signature.
*/
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 /* PUBKEY */
/*
* Default - Fall through for requests; for unknown responses,
* flag as error.
*/
default:
if (opcode & CRYPTO_RESP)
opcode |= CRYPTO_ERROR;
break;
}
/*
* Round up the field length to a multiple of 8 octets and save
* the request code and length.
*/
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
/*
* crypto_setup - load private key, public key, optional agreement
* parameters and optional leapseconds table, then initialize extension
* fields for later signatures.
*/
void
crypto_setup(void)
{
char filename[MAXFILENAME];
u_int fstamp; /* filestamp */
u_int len, temp;
u_int32 *pp;
/*
* Initialize structures.
*/
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;
/*
* Load required private key from file, default "ntpkey".
*/
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)));
/*
* Load required public key from file, default
* "ntpkey_host", where "host" is the canonical name of this
* machine.
*/
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;
/*
* Assemble public key and host name in network byte order.
* These data will later be signed and sent in response to
* a client request. Note that the modulus must be a u_int32 in
* network byte order independent of the host order or u_int
* size.
*/
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);
/*
* Load optional certificate from file, default "ntpkey_certif".
* If the file is missing or defective, the values can later be
* retrieved from a server.
*/
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);
/*
* Load optional agreement parameters from file, default
* "ntpkey_dh". If the file is missing or defective, the values
* can later be retrieved from a server.
*/
if (dh_params_file == NULL)
dh_params_file = "ntpkey_dh";
crypto_dh(dh_params_file);
/*
* Load optional leapseconds from file, default "ntpkey_leap".
* If the file is missing or defective, the values can later be
* retrieved from a server.
*/
if (tai_leap_file == NULL)
tai_leap_file = "ntpkey_leap";
crypto_tai(tai_leap_file);
}
/*
* crypto_agree - compute new public value and sign extension fields.
*/
void
crypto_agree(void)
{
R_RANDOM_STRUCT randomstr; /* wiggle bits */
R_SIGNATURE_CTX ctx; /* signature context */
l_fp lstamp; /* NTP time */
tstamp_t tstamp; /* seconds timestamp */
u_int len, temp;
int rval, i;
/*
* Sign host name and timestamps, but only if the clock is
* synchronized.
*/
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);
/*
* Sign certificate and timestamps.
*/
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);
}
/*
* Sign agreement parameters and timestamps.
*/
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);
/*
* Compute public value.
*/
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);
}
/*
* Sign public value and timestamps.
*/
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);
}
/*
* Sign leapseconds table and timestamps.
*/
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
}
/*
* crypto_rsa - read RSA key, decode and check for errors.
*/
static u_int
crypto_rsa(
char *cp, /* file name */
u_char *key, /* key pointer */
u_int keylen /* key length */
)
{
FILE *str; /* file handle */
u_char buf[MAX_LINLEN]; /* file line buffer */
u_char encoded_key[MAX_ENCLEN]; /* encoded key buffer */
char filename[MAXFILENAME]; /* name of parameter file */
char linkname[MAXFILENAME]; /* file link (for filestamp) */
u_int fstamp; /* filestamp */
u_int bits, len;
char *rptr;
int rval;
/*
* Open the file and discard comment lines. If the first
* character of the file name is not '/', prepend the keys
* directory string.
*/
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);
}
/*
* Ignore initial comments and empty lines.
*/
while ((rptr = fgets(buf, MAX_LINLEN - 1, str)) != NULL) {
len = strlen(buf);
if (len < 1)
continue;
if (*buf == '#' || *buf == '\r' || *buf == '\0')
continue;
break;
}
/*
* We are rather paranoid here, since an intruder might cause a
* coredump by infiltrating a naughty key. The line must contain
* a single integer followed by a PEM encoded, null-terminated
* string.
*/
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);
/*
* Extract filestamp if present.
*/
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);
}
/*
* crypto_cert - read certificate
*/
static void
crypto_cert(
char *cp /* file name */
)
{
u_char buf[5000]; /* file line buffer */
char filename[MAXFILENAME]; /* name of certificate file */
char linkname[MAXFILENAME]; /* file link (for filestamp) */
u_int fstamp; /* filestamp */
u_int32 *pp;
u_int len;
char *rptr;
int rval, fd;
/*
* Open the file and discard comment lines. If the first
* character of the file name is not '/', prepend the keys
* directory string. If the file is not found, not to worry; it
* can be retrieved over the net. But, if it is found with
* errors, we crash and burn.
*/
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;
}
/*
* We are rather paranoid here, since an intruder might cause a
* coredump by infiltrating naughty values.
*/
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);
}
/*
* The extension field entry consists of the raw certificate.
*/
certif.vallen = htonl(200); /* xxxxxxxxxxxxxxxxxx */
pp = emalloc(len);
certif.ptr = (u_char *)pp;
memcpy(pp, buf, len);
certif.sig = emalloc(private_key.bits / 8);
crypto_flags |= CRYPTO_FLAG_CERT;
/*
* Extract filestamp if present.
*/
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
}
/*
* crypto_dh - read agreement parameters, decode and check for errors.
*/
static void
crypto_dh(
char *cp /* file name */
)
{
FILE *str; /* file handle */
u_char buf[MAX_LINLEN]; /* file line buffer */
u_char encoded_key[MAX_ENCLEN]; /* encoded key buffer */
u_char prime[MAX_KEYLEN]; /* decoded prime */
u_char generator[MAX_KEYLEN]; /* decode generator */
u_int primelen; /* prime length (octets) */
u_int generatorlen; /* generator length (octets) */
char filename[MAXFILENAME]; /* name of parameter file */
char linkname[MAXFILENAME]; /* file link (for filestamp) */
u_int fstamp; /* filestamp */
u_int32 *pp;
u_int len;
char *rptr;
int rval;
/*
* Open the file and discard comment lines. If the first
* character of the file name is not '/', prepend the keys
* directory string. If the file is not found, not to worry; it
* can be retrieved over the net. But, if it is found with
* errors, we crash and burn.
*/
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;
}
/*
* Ignore initial comments and empty lines.
*/
while ((rptr = fgets(buf, MAX_LINLEN - 1, str)) != NULL) {
if (strlen(buf) < 1)
continue;
if (*buf == '#' || *buf == '\r' || *buf == '\0')
continue;
break;
}
/*
* We are rather paranoid here, since an intruder might cause a
* coredump by infiltrating a naughty key. There must be two
* lines; the first contains the prime, the second the
* generator. Each line must contain a single integer followed
* by a PEM encoded, null-terminated string.
*/
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);
/*
* Initialize agreement parameters and extension field in
* network byte order. Note the private key length is set
* arbitrarily at half the prime length.
*/
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;
/*
* Initialize public value extension field.
*/
dhpub.vallen = htonl(dh_params.primeLen);
dhpub.ptr = emalloc(dh_params.primeLen);
dhpub.sig = emalloc(private_key.bits / 8);
/*
* Extract filestamp if present.
*/
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
}
/*
* crypto_tai - read leapseconds table and check for errors.
*/
static void
crypto_tai(
char *cp /* file name */
)
{
FILE *str; /* file handle */
u_char buf[MAX_LINLEN]; /* file line buffer */
u_int leapsec[MAX_LEAP]; /* NTP time at leaps */
u_int offset; /* offset at leap (s) */
char filename[MAXFILENAME]; /* name of leapseconds file */
char linkname[MAXFILENAME]; /* file link (for filestamp) */
u_int fstamp; /* filestamp */
u_int32 *pp;
u_int len;
char *rptr;
int rval, i;
#ifdef KERNEL_PLL
#if NTP_API > 3
struct timex ntv; /* kernel interface structure */
#endif /* NTP_API */
#endif /* KERNEL_PLL */
/*
* Open the file and discard comment lines. If the first
* character of the file name is not '/', prepend the keys
* directory string. If the file is not found, not to worry; it
* can be retrieved over the net. But, if it is found with
* errors, we crash and burn.
*/
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;
}
/*
* We are rather paranoid here, since an intruder might cause a
* coredump by infiltrating naughty values. Empty lines and
* comments are ignored. Other lines must begin with two
* integers followed by junk or comments. The first integer is
* the NTP seconds of leap insertion, the second is the offset
* of TAI relative to UTC after that insertion. The second word
* must equal the initial insertion of ten seconds on 1 January
* 1972 plus one second for each succeeding insertion.
*/
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);
}
/*
* The extension field table entries consists of the NTP seconds
* of leap insertion in reverse order, so that the most recent
* insertion is the first entry in the table.
*/
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 /* NTP_API */
#endif /* KERNEL_PLL */
/*
* Extract filestamp if present.
*/
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
}
/*
* crypto_config - configure crypto data from crypto configuration
* command.
*/
void
crypto_config(
int item, /* configuration item */
char *cp /* file name */
)
{
switch (item) {
/*
* Initialize flags
*/
case CRYPTO_CONF_FLAGS:
sscanf(cp, "%x", &crypto_flags);
break;
/*
* Set private key file name.
*/
case CRYPTO_CONF_PRIV:
private_key_file = emalloc(strlen(cp) + 1);
strcpy(private_key_file, cp);
break;
/*
* Set public key file name.
*/
case CRYPTO_CONF_PUBL:
public_key_file = emalloc(strlen(cp) + 1);
strcpy(public_key_file, cp);
break;
/*
* Set certificate file name.
*/
case CRYPTO_CONF_CERT:
certif_file = emalloc(strlen(cp) + 1);
strcpy(certif_file, cp);
break;
/*
* Set agreement parameter file name.
*/
case CRYPTO_CONF_DH:
dh_params_file = emalloc(strlen(cp) + 1);
strcpy(dh_params_file, cp);
break;
/*
* Set leapseconds table file name.
*/
case CRYPTO_CONF_LEAP:
tai_leap_file = emalloc(strlen(cp) + 1);
strcpy(tai_leap_file, cp);
break;
/*
* Set crypto keys directory.
*/
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 /* PUBKEY */
#else
int ntp_crypto_bs_autokey;
#endif /* AUTOKEY */