aboutsummaryrefslogtreecommitdiff
path: root/contrib/ntp/ntpd/ntp_crypto.c
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/ntp/ntpd/ntp_crypto.c')
-rw-r--r--contrib/ntp/ntpd/ntp_crypto.c2060
1 files changed, 2060 insertions, 0 deletions
diff --git a/contrib/ntp/ntpd/ntp_crypto.c b/contrib/ntp/ntpd/ntp_crypto.c
new file mode 100644
index 000000000000..d8516b5ebbae
--- /dev/null
+++ b/contrib/ntp/ntpd/ntp_crypto.c
@@ -0,0 +1,2060 @@
+/*
+ * 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;
+ if (ntp_adjtime(&ntv) == TIME_ERROR)
+ msyslog(LOG_ERR,
+ "kernel TAI update failed");
+#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 */