2059 lines
53 KiB
C
2059 lines
53 KiB
C
/*
|
|
* 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;
|
|
(void)ntp_adjtime(&ntv);
|
|
#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 */
|