/*
 * 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 */
