]> git.ipfire.org Git - thirdparty/ntp.git/commitdiff
Integrate Dave's certificate changes.
authorHarlan Stenn <stenn@ntp.org>
Sat, 23 Mar 2002 06:07:49 +0000 (01:07 -0500)
committerHarlan Stenn <stenn@ntp.org>
Sat, 23 Mar 2002 06:07:49 +0000 (01:07 -0500)
bk: 3c9c1bb5IplTt5Z_etMFsxy3tbOxvg

13 files changed:
include/ntp.h
include/ntp_config.h
include/ntp_control.h
include/ntp_crypto.h
include/ntpd.h
libntp/statestr.c
ntpd/ntp_config.c
ntpd/ntp_control.c
ntpd/ntp_crypto.c
ntpd/ntp_peer.c
ntpd/ntp_proto.c
ntpd/ntp_timer.c
util/genkeys.c

index 2546763a35dca4bf1d6bcf5014eb3cb5020532e7..45f1daa431efc0dc3e89bb20a2592c948648fceb 100644 (file)
@@ -1,12 +1,14 @@
 /*
  * ntp.h - NTP definitions for the masses
  */
-
 #ifndef NTP_H
 #define NTP_H
 
 #include "ntp_types.h"
 #include <math.h>
+#ifdef OPENSSL
+#include "ntp_crypto.h"
+#endif /* OPENSSL */
 
 /*
  * Calendar arithmetic - contributed by G. Healton
@@ -154,16 +156,47 @@ struct autokey {          /* network byte order */
 /*
  * The value structure holds variable length data such as public
  * key, agreement parameters, public valule and leapsecond table.
+ * They are in network byte order.
  */
 struct value {                 /* network byte order */
        tstamp_t tstamp;        /* timestamp */
        tstamp_t fstamp;        /* filestamp */
        u_int32 vallen;         /* value length */
-       u_int32 pkt[1];         /* start of value field */
        u_char  *ptr;           /* data pointer (various) */
        u_int32 siglen;         /* signature length */
        u_char  *sig;           /* signature */
 };
+
+/*
+ * The packet extension field structures are used to hold values
+ * and signatures in network byte order.
+ */
+struct exten {
+       u_int32 opcode;         /* opcode */
+       u_int32 associd;        /* association ID */
+       u_int32 tstamp;         /* timestamp */
+       u_int32 fstamp;         /* filestamp */
+       u_int32 vallen;         /* value length */
+       u_int32 pkt[1];         /* start of value field */
+};
+
+/*
+ * The certificate info/value structure
+ */
+struct cert_info {
+       struct cert_info *link; /* forward link */
+       u_int   flags;          /* flags that wave */
+       EVP_PKEY *pkey;         /* generic key */
+       long    cert_version;   /* X509 version */
+       int     nid;            /* signature/digest ID */
+       EVP_MD  *digest;        /* message digest algorithm */
+       u_long  serial;         /* serial number */
+       tstamp_t first;         /* valid not before */
+       tstamp_t last;          /* valid not after */
+       u_char  *subject;       /* subject common name */
+       u_char  *issuer;        /* issuer common name */
+       struct value cert;      /* certificate/value */
+};
 #endif /* OPENSSL */
 
 /*
@@ -263,15 +296,16 @@ struct peer {
 #define clear_to_zero assoc
        associd_t assoc;        /* peer association ID */
        u_int32 crypto;         /* peer status word */
-       struct  value cinfo;    /* certificate information */
-       u_char  *keystr;        /* host name */
-       u_char  *digest;        /* message digest (EVP_MD) */
+       EVP_PKEY *pkey;         /* public key */
+       EVP_MD  *digest;        /* message digest algorithm */
+       u_char  *subject;       /* certificate subject name */
+       u_char  *issuer;        /* certificate issuer name */
        keyid_t pkeyid;         /* previous key ID */
        keyid_t pcookie;        /* peer cookie */
        struct value cookval;   /* cookie values */
        struct value recval;    /* receive autokey values */
        struct value tai_leap;  /* leapseconds values */
-       u_int32 *cmmd;          /* extension field pointer */
+       struct exten *cmmd;     /* extension pointer */
 
        /*
         * Variables used by authenticated server
index 98a32efee3e7472a63700b3633de818c9ab433f8..eaee048933d0c971d38d568f57b021b1466cd77f 100644 (file)
 #define CONF_CRYPTO_LEAP       3
 #define CONF_CRYPTO_CERT       4
 #define CONF_CRYPTO_RAND       5
+#define CONF_CRYPTO_TRUST      6
 #endif /* OPENSSL */
index 5bd605ef216b876eb6f170753a48b299392b147d..9ec84dcdc13998daca7c1b30eb370177f0c7e067 100644 (file)
@@ -221,12 +221,11 @@ struct ntp_control {
 #ifdef OPENSSL
 #define CP_FLAGS       38
 #define CP_HOST                39
-#define        CP_CERTIF       40
-#define        CP_SESKEY       41
-#define        CP_INITSEQ      42
-#define        CP_INITKEY      43
-#define        CP_INITTSP      44
-#define        CP_DIGEST       45
+#define        CP_SESKEY       40
+#define        CP_INITSEQ      41
+#define        CP_INITKEY      42
+#define        CP_INITTSP      43
+#define        CP_DIGEST       44
 #define        CP_MAXCODE      CP_DIGEST
 #else
 #define        CP_MAXCODE      CP_VARLIST
index 6bf792a05f6400cc65d699368418ce769b2b6f1e..b919cf39b4a4633e3ca72985b1cce970056c2c3c 100644 (file)
  * The following bits are used by the client during the protocol
  * exchange.
  */
-#define CRYPTO_FLAG_PROV  0x0010 /* certificate verified */
-#define CRYPTO_FLAG_AGREE 0x0020 /* cookie verifed */
-#define CRYPTO_FLAG_AUTO  0x0040 /* autokey verified */
-#define CRYPTO_FLAG_LEAP  0x0080 /* leapseconds table verified */
+#define CRYPTO_FLAG_PROV  0x0100 /* certificate verified */
+#define CRYPTO_FLAG_AGREE 0x0200 /* cookie verifed */
+#define CRYPTO_FLAG_AUTO  0x0400 /* autokey verified */
+#define CRYPTO_FLAG_LEAP  0x0800 /* leapseconds table verified */
+#define        CRYPTO_FLAG_VRFY  0x1000 /* signed certificate verified */
+#define CRYPTO_FLAG_TRUST 0x2000 /* someone set the trust bit */
+
+/*
+ * The following flags are used for certificate management.
+ */
+#define CERT_SIGN       0x0001  /* certificate is signed */
+#define CERT_VALID      0x0002  /* certificate is valid */
 
 /*
  * Extension field definitions
@@ -36,6 +44,7 @@
 #define CRYPTO_COOK    CRYPTO_CMD(3) /* cookie value */
 #define CRYPTO_AUTO    CRYPTO_CMD(4) /* autokey values */
 #define CRYPTO_TAI     CRYPTO_CMD(5) /* leapseconds table */
+#define        CRYPTO_SIGN     CRYPTO_CMD(6) /* certificate sign */
 #define CRYPTO_RESP    0x80000000 /* response */
 #define CRYPTO_ERROR   0x40000000 /* error */
 
  */
 #define XEVNT_CMD(x)   (CRPT_EVENT | (x))
 #define XEVNT_OK       XEVNT_CMD(0) /* success */
-#define XEVNT_LEN      XEVNT_CMD(1) /* bad field length */
+#define XEVNT_LEN      XEVNT_CMD(1) /* bad field format or length */
 #define XEVNT_TSP      XEVNT_CMD(2) /* bad timestamp */
 #define XEVNT_FSP      XEVNT_CMD(3) /* bad filestamp */
-#define XEVNT_PUB      XEVNT_CMD(4) /* bad public key */
+#define XEVNT_PUB      XEVNT_CMD(4) /* bad or missing public key */
 #define XEVNT_MD       XEVNT_CMD(5) /* unsupported digest type */
 #define XEVNT_KEY      XEVNT_CMD(6) /* mismatched digest types */
 #define XEVNT_SGL      XEVNT_CMD(7) /* bad signature length */
 #define XEVNT_SIG      XEVNT_CMD(8) /* signature not verified */
-#define XEVNT_SBJ      XEVNT_CMD(9) /* subject hostname mismatch */
-#define XEVNT_PER      XEVNT_CMD(10) /* time not verified */
-#define XEVNT_CRYPT    XEVNT_CMD(11) /* bad cookie encrypt */
-#define XEVNT_DAT      XEVNT_CMD(12) /* bad TAI data */
-
+#define XEVNT_VFY      XEVNT_CMD(9) /* certificate not verified */
+#define XEVNT_PER      XEVNT_CMD(10) /* certificate expired */
+#define XEVNT_CKY      XEVNT_CMD(11) /* bad or missing cookie */
+#define XEVNT_DAT      XEVNT_CMD(12) /* bad or missing leapseconds table */
+#define XEVNT_CRT      XEVNT_CMD(13) /* bad or missing certificate */
 /*
  * Configuration codes
  */
 #define CRYPTO_CONF_KEYS  4    /* keys directory path */
 #define CRYPTO_CONF_CERT  5    /* certificate file name */
 #define CRYPTO_CONF_RAND  6    /* random seed file name */
-
-/*
- * The certificate information structure holds X.509 data
- */
-struct cert_info {
-       EVP_PKEY *pkey;         /* generic key */
-       long    cert_version;   /* X509 version */
-       int     nid;            /* digest/signature NID */
-       u_long  serial;         /* serial number */
-       u_long  first;          /* valid not before */
-       u_long  last;           /* valid not after */
-       u_char  *subject;       /* subject common name */
-       u_char  *issuer;        /* issuer common name */
-       u_char  *cert;          /* ASN.1 certificate */
-       u_int   cert_len;       /* certificate length */
-       u_long  fstamp;         /* filestamp */
-};
+#define        CRYPTO_CONF_TRST  7     /* specify trust */
 
 /*
  * Cryptographic values
  */
 extern u_int   crypto_flags;   /* status word */
-extern struct value host;      /* host name/public key */
-extern struct value cinfo;     /* host certificate information */
+extern struct value hostval;   /* host name/value */
+extern struct cert_info *cinfo; /* host certificate information */
 extern struct value dhparam;   /* agreement parameters */
 extern struct value dhpub;     /* public value */
 extern struct value tai_leap;  /* leapseconds table */
index 2cbf1cffce579a2465df15121c941dc856a37238..2a4e1b39f72093c3885a61633a4b747d4426701d 100644 (file)
@@ -141,17 +141,16 @@ extern    void    resetmanycast   P((void));
 
 /* ntp_crypto.c */
 #ifdef OPENSSL
-extern void    crypto_recv     P((struct peer *, struct recvbuf *, int));
-extern int     crypto_xmit     P((struct pkt *, int, u_int32 *, keyid_t, u_int));
+extern void    crypto_recv     P((struct peer *, struct recvbuf *));
+extern int     crypto_xmit     P((struct pkt *, int, struct exten *, keyid_t));
 extern keyid_t session_key     P((struct sockaddr_in *, struct sockaddr_in *, keyid_t, keyid_t, u_long));
 extern void    make_keylist    P((struct peer *, struct interface *));
 extern void    key_expire      P((struct peer *));
-extern void    crypto_sign     P((void));
+extern void    crypto_update   P((void));
 extern void    crypto_config   P((int, char *));
 extern void    crypto_setup    P((void));
+extern struct exten *crypto_args P((u_int, associd_t, u_char *));
 extern int     crypto_public   P((struct peer *, u_char *, u_int));
-extern struct cert_info *cert_parse P((u_char *, u_int));
-extern void    cert_free       P((struct cert_info *));
 extern void    value_free      P((struct value *));
 #endif /* OPENSSL */
 
index 3cf7e101a92ed28b7ba86faf706a5b5712d649c4..2ffe534d72208d590054db6a63e595f97f9e556b 100644 (file)
@@ -12,9 +12,6 @@
 #include "ntp_refclock.h"
 #include "ntp_control.h"
 #include "ntp_string.h"
-#ifdef OPENSSL
-#include "ntp_crypto.h"
-#endif /* OPENSSL */
 
 /*
  * Structure for turning various constants into a readable string.
@@ -128,18 +125,19 @@ struct codestring peer_codes[] = {
 static
 struct codestring crypto_codes[] = {
        { XEVNT_OK & ~CRPT_EVENT,       "success" },
-       { XEVNT_LEN & ~CRPT_EVENT,      "bad_field_length" },
+       { XEVNT_LEN & ~CRPT_EVENT,      "bad_field_format_or_length" },
        { XEVNT_TSP & ~CRPT_EVENT,      "bad_timestamp" },
        { XEVNT_FSP & ~CRPT_EVENT,      "bad_filestamp" },
-       { XEVNT_PUB & ~CRPT_EVENT,      "bad_public_key" },
+       { XEVNT_PUB & ~CRPT_EVENT,      "bad_or_missing_public_key" },
        { XEVNT_MD & ~CRPT_EVENT,       "unsupported_digest_type" },
        { XEVNT_KEY & ~CRPT_EVENT,      "mismatched_digest_types" },
        { XEVNT_SGL & ~CRPT_EVENT,      "bad_signature_length" },
        { XEVNT_SIG & ~CRPT_EVENT,      "signature_not_verified" },
-       { XEVNT_SBJ & ~CRPT_EVENT,      "subject_hostname_mismatch" },
-       { XEVNT_PER & ~CRPT_EVENT,      "time_not_verified" },
-       { XEVNT_CRYPT & ~CRPT_EVENT,    "bad_cookie_encrypt" },
-       { XEVNT_DAT & ~CRPT_EVENT,      "bad_TAI_data" },
+       { XEVNT_VFY & ~CRPT_EVENT,      "certificate not verified" },
+       { XEVNT_PER & ~CRPT_EVENT,      "certificate_expired" },
+       { XEVNT_CKY & ~CRPT_EVENT,      "bad_or_missing_cookie" },
+       { XEVNT_DAT & ~CRPT_EVENT,      "bad_or_missing_leapsecond_table" },
+       { XEVNT_DAT & ~CRPT_EVENT,      "bad_or_missing_certificate" },
        { -1,                           "crypto" }
 };
 #endif /* OPENSSL */
index f14c3d1adb5a592b01e5b986d11d394b10ef8ce0..da926c23da420c3a0c1613975c5b76389ba92fb1 100644 (file)
 #include "ntp_config.h"
 #include "ntp_cmdargs.h"
 
-#ifdef OPENSSL
-# include "ntp_crypto.h"
-#endif /* OPENSSL */
-
 #include <stdio.h>
 #include <ctype.h>
 #ifdef HAVE_SYS_PARAM_H
@@ -301,6 +297,7 @@ static struct keyword crypto_keywords[] = {
        { "rsakey",             CONF_CRYPTO_RSA },
        { "certificate",        CONF_CRYPTO_CERT },
        { "randfile",           CONF_CRYPTO_RAND },
+       { "trusted",            CONF_CRYPTO_TRUST },
        { "",                   CONFIG_UNKNOWN }
 };
 #endif /* OPENSSL */
@@ -1126,6 +1123,10 @@ getconfig(
                                crypto_config(CRYPTO_CONF_RAND, tokens[i]);
                                break;
 
+                           case CONF_CRYPTO_TRUST:
+                               crypto_config(CRYPTO_CONF_TRST, tokens[i]);
+                               break;
+
                            default:
                                msyslog(LOG_ERR, "crypto: unknown keyword");
                                break;
index 31cf4a5232e1c136cc5dc565bd2cb9b492c90629..6d1f6ceca799aee269bb8170c80ea0de15ddbc71 100644 (file)
 #include <netinet/in.h>
 #include <arpa/inet.h>
 
-#ifdef OPENSSL
-#include "ntp_crypto.h"
-#endif /* OPENSSL */
-
 /*
  * Structure to hold request procedure information
  */
@@ -118,7 +114,7 @@ static struct ctl_var sys_var[] = {
        { CS_FLAGS,     RO, "flags" },          /* 20 */
        { CS_HOST,      RO, "hostname" },       /* 21 */
        { CS_PUBLIC,    RO, "hostkey" },        /* 22 */
-       { CS_CERTIF,    RO, "certificate" },    /* 23 */
+       { CS_CERTIF,    RO, "cert" },           /* 23 */
        { CS_REVTIME,   RO, "refresh" },        /* 24 */
        { CS_LEAPTAB,   RO, "leapseconds" },    /* 25 */
        { CS_TAI,       RO, "tai" },            /* 26 */
@@ -156,10 +152,10 @@ static    u_char def_sys_var[] = {
        CS_HOST,
        CS_DIGEST,
        CS_FLAGS,
-       CS_CERTIF,
        CS_PUBLIC,
        CS_REVTIME,
        CS_LEAPTAB,
+       CS_CERTIF,
 #endif /* OPENSSL */
        0
 };
@@ -210,14 +206,13 @@ static struct ctl_var peer_var[] = {
 #ifdef OPENSSL
        { CP_FLAGS,     RO, "flags" },          /* 38 */
        { CP_HOST,      RO, "hostname" },       /* 39 */
-       { CP_CERTIF,    RO, "certificate" },    /* 40 */
-       { CP_SESKEY,    RO, "cookie" },         /* 41 */
-       { CP_INITSEQ,   RO, "initsequence" },   /* 42 */
-       { CP_INITKEY,   RO, "initkey" },        /* 43 */
-       { CP_INITTSP,   RO, "timestamp" },      /* 44 */
-       { CP_DIGEST,    RO, "signature" },      /* 45 */
+       { CP_SESKEY,    RO, "cookie" },         /* 40 */
+       { CP_INITSEQ,   RO, "initsequence" },   /* 41 */
+       { CP_INITKEY,   RO, "initkey" },        /* 42 */
+       { CP_INITTSP,   RO, "timestamp" },      /* 43 */
+       { CP_DIGEST,    RO, "signature" },      /* 44 */
 #endif /* OPENSSL */
-       { 0,            EOV, "" }               /* 38/46 */
+       { 0,            EOV, "" }               /* 38/44 */
 };
 
 
@@ -259,7 +254,6 @@ static u_char def_peer_var[] = {
        CP_HOST,
        CP_DIGEST,
        CP_FLAGS,
-       CP_CERTIF,
        CP_SESKEY,
        CP_INITSEQ,
 #endif /* OPENSSL */
@@ -1135,7 +1129,6 @@ ctl_putarray(
        register const char *cq;
        char buffer[200];
        int i;
-
        cp = buffer;
        cq = tag;
        while (*cq != '\0')
@@ -1163,6 +1156,10 @@ ctl_putsys(
 {
        l_fp tmp;
        char str[256];
+#ifdef OPENSSL
+       struct cert_info *cp;
+       char cbuf[256];
+#endif /* OPENSSL */
 
        switch (varid) {
 
@@ -1351,19 +1348,24 @@ ctl_putsys(
                break;
 
        case CS_CERTIF:
-               if (cinfo.fstamp != 0)
-                       ctl_putuint(sys_var[CS_CERTIF].text, cinfo.fstamp);
+               for (cp = cinfo; cp != NULL; cp = cp->link) {
+                       sprintf(cbuf, "%s %s %c %u", cp->subject,
+                           cp->issuer, cp->flags & CERT_VALID ? 'T' : 'U',
+                           ntohl(cp->cert.fstamp));
+                       ctl_putstr(sys_var[CS_CERTIF].text, cbuf,
+                           strlen(cbuf));
+               }
                break;
 
        case CS_PUBLIC:
-               if (host.fstamp != 0)
-                       ctl_putuint(sys_var[CS_PUBLIC].text, host.fstamp);
+               if (hostval.fstamp != 0)
+                       ctl_putuint(sys_var[CS_PUBLIC].text, hostval.fstamp);
                break;
 
        case CS_REVTIME:
-               if (host.tstamp != 0)
+               if (hostval.tstamp != 0)
                        ctl_putuint(sys_var[CS_REVTIME].text,
-                           ntohl(host.tstamp));
+                           ntohl(hostval.tstamp));
                break;
 
        case CS_LEAPTAB:
@@ -1626,15 +1628,9 @@ ctl_putpeer(
                break;
 
        case CP_HOST:
-               if (peer->keystr != NULL)
-                       ctl_putstr(peer_var[CP_HOST].text, peer->keystr,
-                           strlen(peer->keystr));
-               break;
-
-       case CP_CERTIF:
-               if (peer->cinfo.fstamp != 0)
-                       ctl_putuint(peer_var[CP_CERTIF].text,
-                           peer->cinfo.fstamp);
+               if (peer->subject != NULL)
+                       ctl_putstr(peer_var[CP_HOST].text, peer->subject,
+                           strlen(peer->subject));
                break;
 
        case CP_SESKEY:
index b9c4acb293b5055717d35b9bdc4cd041db5f830e..19f965e6e8752c7cc22bd88d8855316aeb21e484 100644 (file)
 #define MAX_LEAP       100     /* max UTC leapseconds (s) */
 #define MIN_HOSTLEN    4       /* min host name length */
 #define MAX_HOSTLEN    256     /* max host name length */
-#define        DIGESTNAME      "MD5"   /* message digest algorithm name */
 #define VALUE_LEN      (6 * 4) /* min response field length */
+#define YEAR           (60 * 60 * 24 * 365) /* seconds in year */
 
 /*
  * Global cryptodata in host byte order
  */
-u_int32        crypto_flags;           /* status word */
+u_int32        crypto_flags = 0x0;     /* status word */
 u_int  sys_tai;                /* current UTC offset from TAI */
 
 /*
  * Global cryptodata in network byte order
  */
-struct value host;             /* rsa key */
-struct value cinfo;            /* certificate information */
+struct value hostval;          /* host value */
+struct cert_info *cinfo = NULL;        /* certificate info/value */
 struct value pubkey;           /* public key */
 struct value tai_leap;         /* leapseconds table */
 
 /*
  * Private cryptodata in host byte order
  */
-static EVP_PKEY *rsa_pkey = NULL; /* rsa key */
+static EVP_PKEY *rsa_pkey = NULL; /* host key */
 static EVP_PKEY *sign_pkey = NULL; /* sign key */
+static EVP_MD  *sign_digest = NULL; /* sign digest */
 static u_int sign_siglen;      /* sign key length */
-static const EVP_MD *digest = NULL; /* message digest algorithm */
 static char *keysdir = NTP_KEYSDIR; /* crypto keys directory */
 static char *rand_file = NULL; /* random seed file */
 static char *rsa_file = NULL;  /* rsa key file */
@@ -122,16 +122,21 @@ static char *leap_file = NULL;    /* leapseconds file */
 /*
  * Cryptotypes
  */
-static EVP_PKEY *crypto_key    P((char *, struct value *));
-static struct cert_info *crypto_cert P((char *));
-static void    crypto_tai      P((char *));
-static u_long  asn2ntp         P((ASN1_TIME *));
-static struct value * crypto_verify P((u_int32 *, struct value *,
+static int     crypto_verify   P((struct exten *, struct value *,
                                    struct peer *));
-static u_int   crypto_send     P((struct value *, struct value *));
-static int     crypto_encrypt  P((struct value *, struct value *,
+static int     crypto_encrypt  P((struct exten *, struct value *,
                                    keyid_t *));
-
+static u_int   crypto_send     P((struct exten *, struct value *));
+static tstamp_t crypto_time    P((void));
+static u_long  asn2ntp         P((ASN1_TIME *));
+static struct cert_info *cert_parse P((u_char *, u_int, tstamp_t));
+static int     cert_sign       P((struct exten *, struct value *));
+static int     cert_valid      P((struct cert_info *, EVP_PKEY *));
+static int     cert_install    P((struct exten *, struct peer *));
+static void    cert_free       P((struct cert_info *));
+static EVP_PKEY *crypto_key    P((char *, tstamp_t *));
+static struct cert_info *crypto_cert P((char *));
+static void    crypto_tai      P((char *));
 
 /*
  * session_key - generate session key
@@ -140,8 +145,10 @@ static     int     crypto_encrypt  P((struct value *, struct value *,
  * 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.
+ *
+ * Returns the next key ID
  */
-keyid_t                                /* returns next key ID */
+keyid_t
 session_key(
        struct sockaddr_in *srcadr, /* source address */
        struct sockaddr_in *dstadr, /* destination address */
@@ -200,7 +207,7 @@ make_keylist(
        )
 {
        EVP_MD_CTX ctx;         /* signature context */
-       l_fp    tstamp;         /* NTP timestamp */
+       tstamp_t tstamp;        /* NTP timestamp */
        struct autokey *ap;     /* autokey pointer */
        struct value *vp;       /* value pointer */
        keyid_t keyid;          /* next key ID */
@@ -212,9 +219,7 @@ make_keylist(
        /*
         * Allocate the key list if necessary.
         */
-       L_CLR(&tstamp);
-       if (sys_leap != LEAP_NOTINSYNC)
-               get_systime(&tstamp);
+       tstamp = crypto_time();
        if (peer->keylist == NULL)
                peer->keylist = emalloc(sizeof(keyid_t) *
                    NTP_MAXSESSION);
@@ -259,7 +264,8 @@ make_keylist(
        /*
         * 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.
+        * careful not to use invalid key media. Use the public values
+        * timestamp as filestamp. 
         */
        vp = &peer->sndval;
        if (vp->ptr == NULL)
@@ -267,21 +273,21 @@ make_keylist(
        ap = (struct autokey *)vp->ptr;
        ap->seq = htonl(peer->keynumber);
        ap->key = htonl(keyid);
-       vp->tstamp = htonl(tstamp.l_ui);
-       vp->fstamp = cinfo.fstamp;
+       vp->tstamp = htonl(tstamp);
+       vp->fstamp = hostval.tstamp;
        vp->vallen = htonl(sizeof(struct autokey));
        vp->siglen = 0;
        if (vp->tstamp != 0) {
                if (vp->sig == NULL)
                        vp->sig = emalloc(sign_siglen);
-               EVP_SignInit(&ctx, digest);
+               EVP_SignInit(&ctx, sign_digest);
                EVP_SignUpdate(&ctx, (u_char *)vp, 12);
                EVP_SignUpdate(&ctx, vp->ptr, sizeof(struct autokey));
                if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
                        vp->siglen = htonl(len);
                peer->flags |= FLAG_ASSOC;
        }
-#if DEBUG
+#ifdef DEBUG
        if (debug)
                printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n",
                    ntohl(ap->seq), ntohl(ap->key), cookie,
@@ -304,17 +310,13 @@ make_keylist(
 void
 crypto_recv(
        struct peer *peer,      /* peer structure pointer */
-       struct recvbuf *rbufp,  /* packet buffer pointer */
-       int     is_org          /* timestamps test */
+       struct recvbuf *rbufp   /* packet buffer pointer */
        )
 {
-       EVP_MD_CTX ctx;         /* signature context */
-       const EVP_MD *dp = NULL; /* message digest pointer */
-       EVP_PKEY *kp = NULL;    /* key pointer */
+       EVP_MD *dp;             /* message digest algorithm */
        u_int32 *pkt;           /* receive packet pointer */
        struct autokey *ap, *bp; /* autokey pointer */
-       struct  cert_info *xp;  /* certificate information */
-       struct value *vp;       /* value pointer */
+       struct exten *ep, *fp;  /* extension pointers */
        int     has_mac;        /* length of MAC field */
        int     authlen;        /* offset of MAC field */
        associd_t associd;      /* association ID */
@@ -323,12 +325,13 @@ crypto_recv(
        u_int   len;            /* extension field length */
        u_int   code;           /* extension field opcode */
        u_int   vallen;         /* value length */
-       u_int   siglen;         /* signature length */
+       struct pkt *rpkt;       /* NTP header pointer */
+       X509    *cert;          /* X509 certificate */
+       u_char  *ptr;
        u_char  statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
        keyid_t cookie;         /* crumbles */
-       int     j, rval;
+       int     rval;
        u_int32 temp32;
-       u_int32 *ptr32;
 #ifdef KERNEL_PLL
 #if NTP_API > 3
        struct timex ntv;       /* kernel interface structure */
@@ -345,11 +348,13 @@ crypto_recv(
         * Packets that fail either test sink without a trace. The
         * association ID is saved only if nonzero.
         */
+       rpkt = &rbufp->recv_pkt;
        authlen = LEN_PKT_NOMAC;
        while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) {
                pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4;
-               code = ntohl(pkt[0]) & 0xffff0000;
-               len = ntohl(pkt[0]) & 0x0000ffff;
+               ep = (struct exten *)pkt;
+               code = ntohl(ep->opcode) & 0xffff0000;
+               len = ntohl(ep->opcode) & 0x0000ffff;
                associd = ntohl(pkt[1]);
 #ifdef DEBUG
                if (debug)
@@ -358,6 +363,10 @@ crypto_recv(
                            peer->crypto, authlen, len, code >> 16,
                            associd);
 #endif
+
+               /*
+                * Check version number and field length.
+                */
                if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8 ||
                    (len < VALUE_LEN && (code & CRYPTO_RESP))) {
                        sys_unknownversion++;
@@ -371,6 +380,10 @@ crypto_recv(
                }
                if (associd != 0)
                        peer->assoc = associd;
+               tstamp = ntohl(ep->tstamp);
+               fstamp = ntohl(ep->fstamp);
+               vallen = ntohl(ep->vallen);
+               rval = XEVNT_OK;
                switch (code) {
 
                /*
@@ -378,33 +391,29 @@ crypto_recv(
                 * association ID. In OpenSSL the signature algorithm is
                 * bound to the digest algorithm, so the NID completely
                 * defines the signature scheme. This the only message
-                * not validated by signature, but its timestamps are
-                * not used by NTP.
-                *
-                * Discard the message if the status word has already
-                * been received or the response does not match the
-                * request..
+                * not validated by signature.
                 */
                case CRYPTO_ASSOC | CRYPTO_RESP:
-                       if (peer->crypto || !is_org)
+
+                       /*
+                        * Discard the message if the status word has
+                        * already been received or the response does
+                        * not match the request.
+                        */
+                       if (peer->crypto || peer->flash & TEST2)
                                break;
-                       vp = (struct value *)&pkt[2];
-                       fstamp = ntohl(vp->fstamp);
-                       vallen = ntohl(vp->vallen);
-                       rval = XEVNT_OK;
 
                        /*
                         * We require only that the host name length be
                         * reasonable and the signature digest NID be
-                        * valid. Timestamps and filestamps are
-                        * irrelevant at this time.
+                        * valid.
                         */
                        temp32 = fstamp >> 16;
+                       dp = (EVP_MD *)EVP_get_digestbynid(temp32);
                        if (vallen < MIN_HOSTLEN || vallen >
                            MAX_HOSTLEN)
-                               rval = XEVNT_SBJ;
-                       else if ((dp = EVP_get_digestbynid(temp32)) ==
-                           NULL)
+                               rval = XEVNT_LEN;
+                       else if (dp == NULL)
                                rval = XEVNT_MD;
                        if (rval != XEVNT_OK) {
                                report_event(rval, peer);
@@ -413,15 +422,16 @@ crypto_recv(
                        }
 
                        /*
-                        * Save signature scheme and host name.
+                        * Save status word, host name and message
+                        * digest.
                         */
-                       (const EVP_MD *)peer->digest = dp;
+                       peer->digest = dp;
                        peer->crypto = fstamp;
-                       peer->keystr = emalloc(vallen + 1);
-                       memcpy(peer->keystr, vp->pkt, vallen);
-                       peer->keystr[vallen] = '\0';
+                       peer->subject = emalloc(vallen + 1);
+                       memcpy(peer->subject, ep->pkt, vallen);
+                       peer->subject[vallen] = '\0';
                        sprintf(statstr, "host %s %s (%u)",
-                           peer->keystr, OBJ_nid2ln(temp32), temp32);
+                           peer->subject, OBJ_nid2ln(temp32), temp32);
                        record_crypto_stats(&peer->srcadr, statstr);
 #ifdef DEBUG
                        if (debug)
@@ -433,66 +443,17 @@ crypto_recv(
                 * Decode X509 certificate in ASN.1 format and extract
                 * the data containing, among other things, subject
                 * name and public key.
-                *
-                * Discard the message if the status word has not been
-                * received or the certificate has already been received
-                * or the response does not match the request.
                 */
                case CRYPTO_CERT | CRYPTO_RESP:
-                       if (!peer->crypto || peer->crypto &
-                           CRYPTO_FLAG_PROV || !is_org)
-                               break;
-                       vp = (struct value *)&pkt[2];
-                       tstamp = ntohl(vp->tstamp);
-                       fstamp = ntohl(vp->fstamp);
-                       vallen = ntohl(vp->vallen);
-                       rval = XEVNT_OK;
 
                        /*
-                        * We require valid certificate, signature
-                        * length and signature. The certificate subject
-                        * name must match the previous host name and
-                        * the public key used to verify the (self-
-                        * signed) signature. The timestamp must be
-                        * within the valid interval according to the
-                        * certificate.
-                        *
-                        * In principle, we do not have to sign this
-                        * message, since the certificate contains only
-                        * public values. We do it anyway, since it
-                        * serves as a good checksum if the certificate
-                        * is not self-signed.
+                        * Install the certificate only if valid.
                         */
-                       j = 5 + (vallen + 3) / 4;
-                       siglen = ntohl(pkt[j++]);
-                       dp = (EVP_MD *)peer->digest;
-                       xp = NULL;
-                       if (tstamp == 0 || tstamp < fstamp) {
-                               rval = XEVNT_TSP;
-                       } else if ((xp = cert_parse((u_char *)vp->pkt,
-                           vallen)) == NULL) {
-                               rval = XEVNT_PUB;
-                       } else if ((kp = xp->pkey) == NULL) {
-                               rval = XEVNT_PUB;
-                       } else if (strcmp(xp->subject, peer->keystr) !=
-                           0) {
-                               rval = XEVNT_SBJ;
-                       } else if (tstamp < xp->first || tstamp >
-                           xp->last) {
-                               rval = XEVNT_PER;
-                       } else if (siglen > EVP_PKEY_size(kp)) {
-                               rval = XEVNT_SGL;
-                       } else {
-                               EVP_VerifyInit(&ctx, dp);
-                               EVP_VerifyUpdate(&ctx, (u_char *)vp,
-                                   vallen + 12);
-                               if (!EVP_VerifyFinal(&ctx,
-                                   (u_char *)&pkt[j], siglen, kp))
-                                       rval = XEVNT_SIG;
-                       }
+                       if (crypto_verify(ep, NULL, peer) != XEVNT_OK)
+                               break;
+
+                       rval = cert_install(ep, peer);
                        if (rval != XEVNT_OK) {
-                               if (xp != NULL)
-                                       cert_free(xp);
                                if (rval != XEVNT_TSP) {
                                        report_event(rval, peer);
                                        peer->flash |= TEST12;
@@ -501,26 +462,105 @@ crypto_recv(
                        }
 
                        /*
-                        * Install certificate variables and light the
-                        * proventic bit. If this certificate updates a
-                        * previous onw, a filestamp may have changed,
-                        * so recompute the signatures.
+                        * If the subject name matches the issuer name,
+                        * this is self-signed certificate. If from a
+                        * primary (stratum 1) server, it is ipso facto
+                        * valid.
                         */
-                       peer->cinfo.tstamp = vp->tstamp;
-                       peer->cinfo.fstamp = vp->fstamp;
-                       peer->cinfo.vallen = sizeof(struct cert_info);
-                       if (peer->cinfo.ptr != NULL)
-                               cert_free((struct cert_info *)
-                                   peer->cinfo.ptr);
-                       peer->cinfo.ptr = (u_char *)xp;
-                       crypto_sign();
-                       peer->crypto |= CRYPTO_FLAG_PROV;
+                       if (strcmp(cinfo->subject, cinfo->issuer) ==
+                           0 && PKT_TO_STRATUM(rpkt->stratum) <= 1) {
+                               cinfo->flags |= CERT_VALID;
+                               peer->crypto |= CRYPTO_FLAG_PROV;
+                       }
+
+                       /*
+                        * We plug in the first public key we find, as
+                        * this is the first certificate sent. However,
+                        * note that this certificate might not be
+                        * signed by the server, so we can't check the
+                        * signature/digest NID.
+                        */
+                       if (peer->pkey == NULL) {
+                               ptr = (u_char *)cinfo->cert.ptr;
+                               cert = d2i_X509(NULL, &ptr,
+                                   ntohl(cinfo->cert.vallen));
+                               peer->pkey = X509_get_pubkey(cert);
+                               X509_free(cert);
+                               peer->issuer =
+                                   emalloc(strlen(cinfo->issuer) + 1);
+                               strcpy(peer->issuer, cinfo->issuer);
+                       }
                        peer->flash &= ~TEST10;
-                       temp32 = ((struct cert_info *)
-                           peer->cinfo.ptr)->nid;
+                       temp32 = cinfo->nid;
                        sprintf(statstr, "cert %s %s (%u) fs %u",
-                           xp->subject, OBJ_nid2ln(temp32), temp32,
-                           ntohl(vp->fstamp));
+                           cinfo->subject, OBJ_nid2ln(temp32), temp32,
+                           ntohl(ep->fstamp));
+                       record_crypto_stats(&peer->srcadr, statstr);
+#ifdef DEBUG
+                       if (debug)
+                               printf("crypto_recv: %s\n", statstr);
+#endif
+                       break;
+
+               /*
+                * X509 certificate sign request in symmetric modes.
+                * Validate the certificate and pass to transmit for
+                * signature. Note that the request can only be sent by
+                * a proventicated machine and thus always has a value
+                * component in the extension field.
+                */
+               case CRYPTO_SIGN:
+
+                       /*
+                        * discard the message if not valid.
+                        */
+                       if (crypto_verify(ep, NULL, peer) != XEVNT_OK)
+                               break;
+
+                       /*
+                        * Pass the extension field to the transmit
+                        * side.
+                        */
+                       fp = emalloc(len);
+                       memcpy(fp, ep, len);
+                       temp32 = CRYPTO_RESP;
+                       fp->opcode |= htonl(temp32);
+                       peer->cmmd = fp;
+                       break;
+
+               /*
+                * X509 certificate sign response. Validate the
+                * certificate signed by the server and install. Later
+                * this can be provided to clients of this server in
+                * lieu of the self-signed certificate in order to
+                * validate the public key.
+                */
+               case CRYPTO_SIGN | CRYPTO_RESP:
+
+                       /*
+                        * Discard the message if not verified with
+                        * respect to the public key timestamp.
+                        */
+                       if (!(peer->crypto & CRYPTO_FLAG_PROV))
+                               break;
+
+                       if (crypto_verify(ep, &pubkey, peer) !=
+                           XEVNT_OK)
+                               break;
+
+                       rval = cert_install(ep, peer);
+                       if (rval != XEVNT_OK) {
+                               if (rval != XEVNT_TSP) {
+                                       report_event(rval, peer);
+                                       peer->flash |= TEST12;
+                               }
+                               break;
+                       }
+                       peer->flash &= ~TEST10;
+                       temp32 = cinfo->nid;
+                       sprintf(statstr, "sign %s %s (%u) fs %u",
+                           cinfo->issuer, OBJ_nid2ln(temp32), temp32,
+                           ntohl(ep->fstamp));
                        record_crypto_stats(&peer->srcadr, statstr);
 #ifdef DEBUG
                        if (debug)
@@ -531,50 +571,48 @@ crypto_recv(
                /*
                 * Roll a random cookie and install in symmetric mode.
                 * Encrypt for the response, which is transmitted later.
-                *
-                * Discard the message if the certificate has not been
-                * received or the cookie has alread been received or
-                * the response does not match the request or another
-                * response has already been queued.
                 */
                case CRYPTO_COOK:
-                       if (!(peer->crypto & CRYPTO_FLAG_PROV) ||
-                           peer->crypto & CRYPTO_FLAG_AGREE ||
-                           !is_org || peer->cmmd != NULL)
+
+                       /*
+                        * Discard the message if not verified.
+                        */
+                       if (!(peer->crypto & CRYPTO_FLAG_PROV))
                                break;
-                       if ((vp = crypto_verify(pkt, &peer->cookval,
-                           peer)) == NULL)
+
+                       if (crypto_verify(ep, NULL, peer) != XEVNT_OK)
                                break;
 
-                       ptr32 = emalloc(len);
-                       memcpy(ptr32, pkt, len);
+                       /*
+                        * The signed message has been verified. Pass
+                        * the extension field to the transmit side.
+                        * Bail out if the cookie has already been
+                        * stored.
+                        */
+                       peer->flash &= ~TEST10;
+                       fp = emalloc(len);
+                       memcpy(fp, ep, len);
                        temp32 = CRYPTO_RESP;
-                       *ptr32 |= htonl(temp32);
-                       RAND_bytes((u_char *)&cookie, 4);
-                       value_free(&peer->encrypt);
-                       if (!crypto_encrypt(&peer->encrypt, vp,
-                           &cookie)) {
-                               report_event(XEVNT_CRYPT, peer);
-                               free(ptr32);
-                               peer->flash |= TEST12;
+                       fp->opcode |= htonl(temp32);
+                       peer->cmmd = fp;
+                       if (peer->crypto & CRYPTO_FLAG_AGREE)
                                break;
-                       }
-                       peer->cmmd = ptr32;
 
                        /*
                         * Install cookie values and light the cookie
-                        * bit.
+                        * bit. The transmit side will pick up and
+                        * encrypt it for the response.
                         */
                        key_expire(peer);
-                       peer->cookval.tstamp = vp->tstamp;
-                       peer->cookval.fstamp = vp->fstamp;
-                       peer->pcookie = cookie;
+                       peer->cookval.tstamp = ep->tstamp;
+                       peer->cookval.fstamp = ep->fstamp;
+                       RAND_bytes((u_char *)&peer->pcookie, 4);
                        peer->crypto &= ~CRYPTO_FLAG_AUTO;
                        peer->crypto |= CRYPTO_FLAG_AGREE;
                        peer->flash &= ~TEST10;
                        sprintf(statstr, "cryp %x ts %u fs %u",
-                           peer->pcookie, ntohl(vp->tstamp),
-                           ntohl(vp->fstamp));
+                           peer->pcookie, ntohl(ep->tstamp),
+                           ntohl(ep->fstamp));
                        record_crypto_stats(&peer->srcadr, statstr);
 #ifdef DEBUG
                        if (debug)
@@ -587,31 +625,32 @@ crypto_recv(
                 * symmetric modes. If the cookie bit is set, the
                 * working cookie is the EXOR of the current and new
                 * values.
-                *
-                * Discard the response if the certificate has not been
-                * received or the response does not match the request.
                 */
                case CRYPTO_COOK | CRYPTO_RESP:
-                       if (!(peer->crypto & CRYPTO_FLAG_PROV) ||
-                           !is_org)
+
+                       /*
+                        * Discard the message if not verified with
+                        * respect to the cookie values.
+                        */
+                       if (!(peer->crypto & CRYPTO_FLAG_PROV))
                                break;
-                       if ((vp = crypto_verify(pkt, &peer->cookval,
-                           peer)) == NULL)
+
+                       else if (crypto_verify(ep, &peer->cookval,
+                           peer) != XEVNT_OK)
                                break;
 
                        /*
                         * Decrypt the cookie.
                         */
-                       vallen = ntohl(vp->vallen);
                        if (vallen == EVP_PKEY_size(rsa_pkey)) {
                                RSA_private_decrypt(vallen,
-                                   (u_char *)vp->pkt,
+                                   (u_char *)ep->pkt,
                                    (u_char *)&temp32,
                                    rsa_pkey->pkey.rsa,
                                    RSA_PKCS1_OAEP_PADDING);
                                cookie = ntohl(temp32);
                        } else {
-                               report_event(XEVNT_LEN, peer);
+                               report_event(XEVNT_CKY, peer);
                                peer->flash |= TEST12;
                                break;
                        }
@@ -622,8 +661,8 @@ crypto_recv(
                         * are done here.
                         */
                        key_expire(peer);
-                       peer->cookval.tstamp = vp->tstamp;
-                       peer->cookval.fstamp = vp->fstamp;
+                       peer->cookval.tstamp = ep->tstamp;
+                       peer->cookval.fstamp = ep->fstamp;
                        if (peer->crypto & CRYPTO_FLAG_AGREE)
                                peer->pcookie ^= cookie;
                        else
@@ -636,8 +675,8 @@ crypto_recv(
                        peer->crypto |= CRYPTO_FLAG_AGREE;
                        peer->flash &= ~TEST10;
                        sprintf(statstr, "cryp %x ts %u fs %u",
-                           peer->pcookie, ntohl(vp->tstamp),
-                           ntohl(vp->fstamp));
+                           peer->pcookie, ntohl(ep->tstamp),
+                           ntohl(ep->fstamp));
                        record_crypto_stats(&peer->srcadr, statstr);
 #ifdef DEBUG
                        if (debug)
@@ -652,16 +691,21 @@ crypto_recv(
                 * rolled. Ordinarily, this is automatic as this message
                 * is piggybacked on the first NTP packet sent upon
                 * either of these events.
-                *
-                * Discard the response if the certificate has not been
-                * received. A broadcast client or symmetric peer can
-                * receive this response without a matching request.
                 */
                case CRYPTO_AUTO | CRYPTO_RESP:
+
+                       /*
+                        * Discard the message if not verified with
+                        * respect to the autokey timestamp. Note that a
+                        * broadcast client or symmetric peer can
+                        * receive this response without a matching
+                        * request.
+                        */
                        if (!(peer->crypto & CRYPTO_FLAG_PROV))
                                break;
-                       if ((vp = crypto_verify(pkt, &peer->recval,
-                           peer)) == NULL)
+
+                       else if (crypto_verify(ep, &peer->recval,
+                           peer) != XEVNT_OK)
                                break;
 
                        /*
@@ -672,9 +716,9 @@ crypto_recv(
                                peer->recval.ptr =
                                    emalloc(sizeof(struct autokey));
                        bp = (struct autokey *)peer->recval.ptr;
-                       peer->recval.tstamp = vp->tstamp;
-                       peer->recval.fstamp = vp->fstamp;
-                       ap = (struct autokey *)vp->pkt;
+                       peer->recval.tstamp = ep->tstamp;
+                       peer->recval.fstamp = ep->fstamp;
+                       ap = (struct autokey *)ep->pkt;
                        bp->seq = ntohl(ap->seq);
                        bp->key = ntohl(ap->key);
                        peer->pkeyid = bp->key;
@@ -682,8 +726,8 @@ crypto_recv(
                        peer->flash &= ~TEST10;
                        sprintf(statstr,
                            "auto seq %d key %x ts %u fs %u", bp->seq,
-                           bp->key, ntohl(vp->tstamp),
-                           ntohl(vp->fstamp));
+                           bp->key, ntohl(ep->tstamp),
+                           ntohl(ep->fstamp));
                        record_crypto_stats(&peer->srcadr, statstr);
 #ifdef DEBUG
                        if (debug)
@@ -699,34 +743,51 @@ crypto_recv(
                 * protocol. While the entire table is installed at the
                 * server, presently only the current TAI offset is
                 * provided via the kernel to other applications.
-                *
-                * Discard the request if the certificate has not been
-                * received or the leapseconds table has already been
-                * received or the response does not match the request
-                * or another response has already been queued. Discard
-                * the response if the certificate has not been received
-                * or the response does not match the request.
                 */
                case CRYPTO_TAI:
-                       if (!(peer->crypto & CRYPTO_FLAG_PROV) ||
-                           peer->crypto & CRYPTO_FLAG_LEAP ||
-                           !is_org || peer->cmmd != NULL)
+
+                       /*
+                        * Discard the message if not verified.
+                        */
+                       if (!(peer->crypto & CRYPTO_FLAG_PROV))
                                break;
-                       ptr32 = emalloc(len);
-                       memcpy(ptr32, pkt, len);
+
+                       if (len > VALUE_LEN) {
+                               if (crypto_verify(ep, NULL, peer) !=
+                                   XEVNT_OK)
+                                       break;
+
+                               peer->flash &= ~TEST10;
+                       }
+
+                       /*
+                        * Pass the extension field to the transmit
+                        * side. Continue below if a leapseconds table
+                        * accompanies the message.
+                        */
+                       peer->flash &= ~TEST10;
+                       fp = emalloc(len);
+                       memcpy(fp, ep, len);
                        temp32 = CRYPTO_RESP;
-                       *ptr32 |= htonl(temp32);
-                       peer->cmmd = ptr32;
+                       fp->opcode |= htonl(temp32);
+                       peer->cmmd = fp;
                        if (len <= VALUE_LEN)
                                break;
+
                        /* fall through */
 
                case CRYPTO_TAI | CRYPTO_RESP:
-                       if (!(peer->crypto & CRYPTO_FLAG_PROV) ||
-                           !is_org)
+
+                       /*
+                        * Discard the message if not certfied or not
+                        * verified with respect to the leapseconds
+                        * values.
+                        */
+                       if (!(peer->crypto & CRYPTO_FLAG_PROV))
                                break;
-                       if ((vp = crypto_verify(pkt, &peer->tai_leap,
-                          peer)) == NULL)
+
+                       if (crypto_verify(ep, &peer->tai_leap, peer) !=
+                           XEVNT_OK)
                                break;
 
                        /*
@@ -735,9 +796,9 @@ crypto_recv(
                         * order. Since a filestamp may have changed,
                         * recompute the signatures.
                         */
-                       peer->tai_leap.tstamp = vp->tstamp;
-                       peer->tai_leap.fstamp = vp->fstamp;
-                       peer->tai_leap.vallen = vp->vallen;
+                       peer->tai_leap.tstamp = ep->tstamp;
+                       peer->tai_leap.fstamp = ep->fstamp;
+                       peer->tai_leap.vallen = ep->vallen;
 
                        /*
                         * Install the new table if there is no stored
@@ -745,16 +806,15 @@ crypto_recv(
                         * the stored table. Since a filestamp may have
                         * changed, recompute the signatures.
                         */
-                       vallen = ntohl(vp->vallen);
                        if (ntohl(peer->tai_leap.fstamp) >
                            ntohl(tai_leap.fstamp)) {
-                               tai_leap.fstamp = vp->fstamp;
-                               tai_leap.vallen = vp->vallen;
+                               tai_leap.fstamp = ep->fstamp;
+                               tai_leap.vallen = ep->vallen;
                                if (tai_leap.ptr != NULL)
                                        free(tai_leap.ptr);
                                tai_leap.ptr = emalloc(vallen);
-                               memcpy(tai_leap.ptr, vp->pkt, vallen);
-                               crypto_sign();
+                               memcpy(tai_leap.ptr, ep->pkt, vallen);
+                               crypto_update();
                                sys_tai = vallen / 4 + TAI_1972 - 1;
                        }
                        crypto_flags |= CRYPTO_FLAG_TAI;
@@ -772,8 +832,8 @@ crypto_recv(
 #endif /* NTP_API */
 #endif /* KERNEL_PLL */
                        sprintf(statstr, "leap %u ts %u fs %u",
-                           vallen, ntohl(vp->tstamp),
-                           ntohl(vp->fstamp));
+                           vallen, ntohl(ep->tstamp),
+                           ntohl(ep->fstamp));
                        record_crypto_stats(&peer->srcadr, statstr);
 #ifdef DEBUG
                        if (debug)
@@ -789,14 +849,15 @@ crypto_recv(
                default:
                        if (code & (CRYPTO_RESP | CRYPTO_ERROR))
                                break;
+
                        if (peer->cmmd != NULL)
                                break;
-                       ptr32 = emalloc(len);
-                       memcpy(ptr32, pkt, len);
+
+                       fp = emalloc(len);
+                       memcpy(fp, ep, len);
                        temp32 = CRYPTO_RESP;
-                       *ptr32 |= htonl(temp32);
-                       peer->cmmd = ptr32;
-                       break;
+                       fp->opcode |= htonl(temp32);
+                       peer->cmmd = fp;
                }
                authlen += len;
        }
@@ -811,23 +872,27 @@ crypto_recv(
  * when one is not. The only case where this matters is to retrieve the
  * autokey information, in which case the caller has to provide the
  * association ID to match the association.
+ *
+ * Returns length of extension field.
  */
-int                            /* return length of extension field */
+int
 crypto_xmit(
        struct pkt *xpkt,       /* transmit packet pointer */
        int     start,          /* offset to extension field */
-       u_int32 *epkt,          /* extension field pointer*/
-       keyid_t cookie,         /* session cookie */
-       u_int   associd         /* association ID */
+       struct exten *ep,       /* extension pointer */
+       keyid_t cookie          /* session cookie */
        )
 {
        u_int32 *pkt;           /* packet pointer */
        struct peer *peer;      /* peer structure pointer */
        u_int   opcode;         /* extension field opcode */
-       struct value *vp;       /* transmit packet value pointer */
-       struct value *ep, *pp;  /* value structure pointer */
+       struct exten *fp;       /* extension pointers */
+       struct cert_info *cp;   /* certificate info/value pointer */
+       char    certname[MAX_HOSTLEN + 1]; /* subject name buffer */
+       u_int   vallen; 
+       struct value vtemp;
+       associd_t associd;
        u_int   len;
-       struct value encryptval;
        keyid_t tcookie;
 
        /*
@@ -835,38 +900,82 @@ crypto_xmit(
         * and association ID.
         */
        pkt = (u_int32 *)xpkt + start / 4;
-       opcode = ntohl(epkt[0]);
-       pkt[1] = htonl(associd);
+       fp = (struct exten *)pkt;
+       opcode = ntohl(ep->opcode);
+       associd = ntohl(ep->associd);
+       fp->associd = htonl(associd);
        len = 8;
-       vp = (struct value *)&pkt[2];
-       ep = (struct value *)&epkt[2];
        switch (opcode & 0xffff0000) {
 
        /*
-        * Send association, status and crypto info. Note, this message
-        * is not signed.
+        * Send association response with status word and crypto info.
+        * Note, this message is not signed and the filestamp contains
+        * only the status word.
         */
        case CRYPTO_ASSOC | CRYPTO_RESP:
-               len += crypto_send(vp, &host);
-               vp->fstamp = htonl(crypto_flags);
+               len += crypto_send(fp, &hostval);
+               fp->fstamp = htonl(crypto_flags);
                break;
 
        /*
-        * Send certificate and signature.
+        * Send certificate request. Use the filestamp from the
+        * extension field.
         */
+       case CRYPTO_CERT:
+               memset(&vtemp, 0, sizeof(vtemp));
+               vtemp.tstamp = ep->tstamp;
+               vtemp.fstamp = ep->fstamp;
+               vtemp.vallen = ep->vallen;
+               vtemp.ptr = (char *)ep->pkt;
+               len += crypto_send(fp, &vtemp);
+               break;
+
+       /*
+        * Send certificate response or sign request. Use the filestamp
+        * from the certificate.
+        */
+       case CRYPTO_SIGN:
        case CRYPTO_CERT | CRYPTO_RESP:
-               len += crypto_send(vp, &cinfo);
+               vallen = ntohl(ep->vallen);
+               if (vallen < MIN_HOSTLEN || vallen > MAX_HOSTLEN) {
+                       opcode |= CRYPTO_ERROR;
+                       break;
+               }
+               memcpy(certname, ep->pkt, vallen);
+               certname[vallen] = '\0';
+               for (cp = cinfo; cp != NULL; cp = cp->link) {
+                       if (strcmp(certname, cp->subject) == 0) {
+                               len += crypto_send(fp, &cp->cert);
+                               break;
+                       }
+               }
+               if (cp == NULL)
+                       opcode |= CRYPTO_ERROR;
+               break;
+
+       /*
+        * Send certificate sign response. Not for sissies. Use the
+        * current time as the filestamp. Light the error bit if invalid
+        * certificate.
+        */
+       case CRYPTO_SIGN | CRYPTO_RESP:
+               if (cert_sign(ep, &vtemp) == XEVNT_OK)
+                       len += crypto_send(fp, &vtemp);
+               else
+                       opcode |= CRYPTO_ERROR;
+               value_free(&vtemp);
                break;
 
        /*
-        * Send public key and signature.
+        * Send public key and signature. Use the public key filestamp.
         */
        case CRYPTO_COOK:
-               len += crypto_send(vp, &pubkey);
+               len += crypto_send(fp, &pubkey);
                break;
 
        /*
-        * Encrypt and send cookie and signature.
+        * Encrypt and send cookie and signature. Light the error bit if
+        * anything goes wrong.
         */
        case CRYPTO_COOK | CRYPTO_RESP:
                if ((opcode & 0xffff) < VALUE_LEN) {
@@ -874,31 +983,27 @@ crypto_xmit(
                        break;
                }
                if (PKT_MODE(xpkt->li_vn_mode) == MODE_SERVER) {
-                       pp = &encryptval;
-                       memset(pp, 0, sizeof(struct value));
                        tcookie = cookie;
-                       if (crypto_encrypt(pp, ep, &tcookie)) {
-                               len += crypto_send(vp, pp);
-                       } else {
-                               opcode |= CRYPTO_ERROR;
-                               break;
-                       }
                } else {
                        if ((peer = findpeerbyassoc(associd)) == NULL) {
                                opcode |= CRYPTO_ERROR;
                                break;
                        }
-                       pp = &peer->encrypt;
+                       tcookie = peer->pcookie;
                }
-               len += crypto_send(vp, pp);
-               value_free(pp);
+               if (crypto_encrypt(ep, &vtemp, &tcookie) == XEVNT_OK)
+                       len += crypto_send(fp, &vtemp);
+               else
+                       opcode |= CRYPTO_ERROR;
+               value_free(&vtemp);
                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.
+        * perp has replayed an old message, in which case light the
+        * error bit.
         */
        case CRYPTO_AUTO | CRYPTO_RESP:
                if ((peer = findpeerbyassoc(associd)) == NULL) {
@@ -906,19 +1011,18 @@ crypto_xmit(
                        break;
                }
                peer->flags &= ~FLAG_ASSOC;
-               len += crypto_send(vp, &peer->sndval);
+               len += crypto_send(fp, &peer->sndval);
                break;
 
        /*
-        * Send leapseconds table and signature.
+        * Send leapseconds table and signature. If no table has been
+        * loaded, just send a request.
         */
        case CRYPTO_TAI:
        case CRYPTO_TAI | CRYPTO_RESP:
-               if (crypto_flags & CRYPTO_FLAG_TAI) {
-                       len += crypto_send(vp, &tai_leap);
-                       break;
-               }
-               /* fall through */
+               if (crypto_flags & CRYPTO_FLAG_TAI)
+                       len += crypto_send(fp, &tai_leap);
+               break;
 
        /*
         * Default - Fall through for requests; for unknown responses,
@@ -935,7 +1039,7 @@ crypto_xmit(
         * the request code and length.
         */
        len = ((len + 7) / 8) * 8;
-       pkt[0] = htonl((opcode & 0xffff0000) | len);
+       fp->opcode = htonl((opcode & 0xffff0000) | len);
 #ifdef DEBUG
        if (debug)
                printf(
@@ -947,41 +1051,170 @@ crypto_xmit(
 
 
 /*
- * crypto_encrypt - encrypt cookie and construct response message. 
+ * crypto_verify - parse and verify the extension field and value
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_LEN   bad field format or length
+ * XEVNT_TSP   bad timestamp
+ * XEVNT_FSP   bad filestamp
+ * XEVNT_PUB   bad or missing public key
+ * XEVNT_SGL   bad signature length
+ * XEVNT_SIG   signature not verified
+ */
+static int
+crypto_verify(
+       struct exten *ep,       /* extension pointer */
+       struct value *vp,       /* value pointer */
+       struct peer *peer       /* peer structure pointer */
+       )
+{
+       EVP_MD_CTX ctx;         /* signature context */
+       tstamp_t tstamp;        /* timestamp */
+       tstamp_t fstamp;        /* filestamp */
+       u_int   vallen;         /* value length */
+       u_int   siglen;         /* signature length */
+       u_char  statstr[NTP_MAXSTRLEN];
+       u_int   opcode, len;
+       int     rval;
+       int     i;
+
+       /*
+        * We require valid opcode, field length, timestamp, filestamp,
+        * public key, signature length and signature. A valid timestamp
+        * is nonzero and greater than the last timestamp received.
+        * Duplicates are valid if the autokey protocol has not
+        * completed.
+        */
+       len = ntohl(ep->opcode) & 0x0000ffff;
+       opcode = ntohl(ep->opcode) & 0xffff0000;
+
+       /*
+        * Check for valid operation code and header length.
+        */
+       if (len < VALUE_LEN)
+               return (XEVNT_LEN);
+       else if (!peer->crypto || opcode & CRYPTO_ERROR)
+               return (XEVNT_LEN);
+       else if (opcode & CRYPTO_RESP && peer->flash & TEST2)
+               return (XEVNT_LEN);
+       else if (!(opcode & CRYPTO_RESP) && peer->cmmd != NULL)
+               return (XEVNT_LEN);
+
+       /*
+        * Check for valid field length and timestamp.
+        */
+       tstamp = ntohl(ep->tstamp);
+       fstamp = ntohl(ep->fstamp);
+       vallen = ntohl(ep->vallen);
+       i = (vallen + 3) / 4;
+       siglen = ntohl(ep->pkt[i++]);
+       rval = XEVNT_OK;
+       if (len < VALUE_LEN + vallen + siglen) {
+               return (XEVNT_LEN);
+       } else if (tstamp == 0 || tstamp < fstamp) {
+               rval = XEVNT_TSP;
+
+       /*
+        * Check for valid timestamp and filestamp in value structure.
+        * Duplicate timestamps are illegal once the cookie has been
+        * received.
+        */
+       } else if (vp != NULL && (tstamp < ntohl(vp->tstamp) ||
+           (tstamp == ntohl(vp->tstamp) && (peer->crypto &
+           CRYPTO_FLAG_AUTO)))) {
+               rval = XEVNT_TSP;
+       } else if (vp != NULL && (tstamp < ntohl(vp->fstamp) || fstamp <
+           ntohl(vp->fstamp))) {
+               rval = XEVNT_FSP;
+
+       /*
+        * Check for valid public key, but only if not a certificate
+        * request.
+        */
+       } else if (peer->pkey == NULL) {
+               /* fall through */
+       } else if (siglen > EVP_PKEY_size(peer->pkey)) {
+               rval = XEVNT_SGL;
+
+       /*
+        * Check for valid signature.
+        */
+       } else {
+               EVP_VerifyInit(&ctx, peer->digest);
+               EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen +
+                   12);
+               if (!EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i],
+                   siglen, peer->pkey))
+                       rval = XEVNT_SIG;
+       }
+#ifdef DEBUG
+       if (debug > 1)
+               printf(
+                   "crypto_recv: verify %x vallen %u siglen %u ts %u fs %u\n",
+                   rval, vallen, siglen, tstamp, fstamp);
+#endif
+
+       /*
+        * We ignore opcode, length and timestamp errors, mostly to
+        * crisp the logs and reduce clogging hazard.
+        */
+       if (rval > XEVNT_TSP) {
+               sprintf(statstr, "error %x code %x ts %u fs %u", rval,
+                   opcode, tstamp, fstamp);
+               record_crypto_stats(&peer->srcadr, statstr);
+               report_event(rval, peer);
+               peer->flash |= TEST12;
+#ifdef DEBUG
+               if (debug)
+                       printf("crypto_verify: %s\n", statstr);
+#endif
+       }
+       return (rval);
+}
+
+/*
+ * crypto_encrypt - construct encrypted cookie and signature from
+ * extension field and cookie
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_PUB   bad or missing public key
+ * XEVNT_CKY   bad or missing cookie
  */
 static int
 crypto_encrypt(
-       struct value *vp,       /* value structure pointer */
-       struct value *ep,       /* value structure pointer */
+       struct exten *ep,       /* extension pointer */
+       struct value *vp,       /* value pointer */
        keyid_t *cookie         /* server cookie */
        )
 {
        EVP_PKEY *rkey;         /* public key */
        EVP_MD_CTX ctx;         /* signature context */
-       l_fp    tstamp;         /* NTP timestamp */
-       u_char  *ptr;
-       u_int   len;
+       tstamp_t tstamp;        /* NTP timestamp */
        u_int32 temp32;
+       u_int   len;
+       u_char  *ptr;
 
        /*
-        * Extract the public key from the request.
+        * Extract the public key from the request extensino field.
         */
+       tstamp = crypto_time();
        ptr = (u_char *)ep->pkt;
-       if ((rkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr,
-           ntohl(ep->vallen))) == NULL) {
+       len = ntohl(ep->vallen);
+       if ((rkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len)) ==
+           NULL) {
                msyslog(LOG_ERR, "crypto_recv %s",
                    ERR_error_string(ERR_get_error(), NULL));
-               return (0);
+               return (XEVNT_PUB);
        }
 
        /*
-        * Encrypt the cookie, construct and sign the response.
+        * Encrypt the cookie, construct and sign the response value.
         */
-       L_CLR(&tstamp);
-       if (sys_leap != LEAP_NOTINSYNC)
-               get_systime(&tstamp);
-       vp->tstamp = htonl(tstamp.l_ui);
-       vp->fstamp = host.fstamp;
+       memset(vp, 0, sizeof(struct value));
+       vp->tstamp = htonl(tstamp);
+       vp->fstamp = hostval.tstamp;
        len = EVP_PKEY_size(rkey);
        vp->vallen = htonl(len);
        vp->ptr = emalloc(len);
@@ -991,121 +1224,69 @@ crypto_encrypt(
                msyslog(LOG_ERR, "crypto_recv %s",
                    ERR_error_string(ERR_get_error(), NULL));
                EVP_PKEY_free(rkey);
-               return (0);
+               return (XEVNT_CKY);
        }
        EVP_PKEY_free(rkey);
        vp->siglen = 0;
        vp->sig = emalloc(sign_siglen);
-       EVP_SignInit(&ctx, digest);
-       EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+       EVP_SignInit(&ctx, sign_digest);
+       EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12);
        EVP_SignUpdate(&ctx, vp->ptr, len);
        if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
                vp->siglen = htonl(len);
-       return (1);
+       return (XEVNT_OK);
 }
 
 
 /*
- * crypto_verify - parse and verify the packet and value structure
+ * crypto_args - construct extension field from arguments
+ *
+ * This routine creates an extension field  with current timestamps and
+ * specified opcode, association ID and optional string. Note that the
+ * extension field is created here, but freed after the crypto_xmit()
+ * call in the protocol module.
+ *
+ * Returns extension field pointer (no errors).
  */
-static struct value *
-crypto_verify(
-       u_int32 *pkt,           /* packet pointer */
-       struct value *up,       /* value structure pointer */
-       struct peer *peer       /* peer structure pointer */
+struct exten *
+crypto_args(
+       u_int   opcode,         /* operation code */
+       associd_t associd,      /* association ID */
+       u_char  *str            /* argument string */
        )
 {
-       EVP_MD_CTX ctx;         /* signature context */
-       EVP_PKEY *kp;           /* key pointer */
-       struct  value *vp;      /* value pointer */
-       struct cert_info *xp;   /* certificate information */
-       tstamp_t tstamp;        /* timestamp */
-       tstamp_t fstamp;        /* filestamp */
-       u_int   vallen;         /* value length */
-       u_int   siglen;         /* signature length */
-       u_char  statstr[NTP_MAXSTRLEN];
-       u_int   code, len;
-       int     rval;
-       int     i;
+       tstamp_t tstamp;        /* NTP timestamp */
+       struct exten *ep;       /* extension field pointer */
+       u_int   len;            /* extension field length */
 
-       /*
-        * We require valid field length, timestamp, public key,
-        * signature length and signature. A valid timestamp is nonzero
-        * and greater than the last timestamp received. Duplicates are
-        * valid if the autokey protocol has not completed. In addition,
-        * timestamps must be within the valid period of the
-        * certificate.
-        */
-       len = ntohl(pkt[0]) & 0x0000ffff;
-       code = ntohl(pkt[0]) & 0xffff0000;
-       rval = XEVNT_OK;
-       vp = (struct value *)&pkt[2];
-       tstamp = ntohl(vp->tstamp);
-       fstamp = ntohl(vp->fstamp);
-       vallen = ntohl(vp->vallen);
-       i = (vallen + 3) / 4;
-       siglen = ntohl(vp->pkt[i++]);
-       if (!peer->crypto || code & CRYPTO_ERROR) {
-               rval = XEVNT_LEN;
-       } else if (len < VALUE_LEN || len < VALUE_LEN + vallen +
-           siglen) {
-               rval = XEVNT_LEN;
-       } else if (tstamp == 0 || tstamp < ntohl(up->tstamp) ||
-           (tstamp == ntohl(up->tstamp) && (peer->crypto &
-           CRYPTO_FLAG_AUTO))) {
-               rval = XEVNT_TSP;
-       } else if (tstamp < ntohl(up->fstamp) || fstamp <
-           ntohl(up->fstamp)) {
-               rval = XEVNT_FSP;
-       } else if (!(peer->crypto & CRYPTO_FLAG_PROV)) {
-               rval = XEVNT_PUB;
-       } else if ((xp = (struct cert_info *)peer->cinfo.ptr) == NULL) {
-               rval = XEVNT_PUB;
-       } else if (tstamp < xp->first || tstamp > xp->last) {
-               rval = XEVNT_TSP;
-       } else if ((kp = xp->pkey) == NULL) {
-               rval = XEVNT_PUB;
-       } else if (siglen > EVP_PKEY_size(kp)) {
-               rval = XEVNT_SGL;
-       } else {
-               EVP_VerifyInit(&ctx, (EVP_MD *)peer->digest);
-               EVP_VerifyUpdate(&ctx, (u_char *)vp, vallen + 12);
-               if (!EVP_VerifyFinal(&ctx, (u_char *)&vp->pkt[i],
-                   siglen, kp))
-                       rval = XEVNT_SIG;
-       }
-#ifdef DEBUG
-       if (debug > 1)
-               printf(
-                   "crypto_recv: verify %x vallen %u siglen %u ts %u fs %u\n",
-                   rval, vallen, siglen, tstamp, fstamp);
-#endif
-       if (rval != XEVNT_OK) {
-               sprintf(statstr, "error %x code %x ts %u fs %u", rval,
-                   code, tstamp, fstamp);
-               if (rval != XEVNT_TSP) {
-                       record_crypto_stats(&peer->srcadr, statstr);
-                       report_event(rval, peer);
-               }
-               peer->flash |= TEST12;
-#ifdef DEBUG
-               if (debug)
-                       printf("crypto_verify: %s\n", statstr);
-#endif
-               return (NULL);
+       tstamp = crypto_time();
+       len = sizeof(struct exten);
+       if (str != NULL)
+               len += strlen(str);
+       ep = emalloc(len);
+       memset(ep, 0, len);
+       ep->opcode = htonl(opcode + len);
+       ep->associd = htonl(associd);
+       ep->tstamp = htonl(tstamp);
+       ep->fstamp = hostval.tstamp;
+       ep->vallen = 0;
+       if (str != NULL) {
+               ep->vallen = htonl(strlen(str));
+               memcpy(ep->pkt, str, strlen(str));
        }
-       return (vp);
+       return (ep);
 }
 
 
 /*
- * crypto_send - concatenate value and signature elements in extension
- * field. We are very conservative here; Jon would be proud.
+ * crypto_send - construct extension field from value components
+ *
+ * Returns extension field length.
  */
 u_int
 crypto_send(
-       struct value *pp,       /* packet pointer */
-       struct value *vp        /* value structure pointer */
+       struct exten *ep,       /* extension field pointer */
+       struct value *vp        /* value pointer */
        )
 {
        u_int   len, temp32;
@@ -1115,115 +1296,116 @@ crypto_send(
         * Copy data. If the data field is empty or zero length, return
         * a value field length of zero.
         */
-       pp->tstamp = vp->tstamp;
-       pp->fstamp = vp->fstamp;
-       pp->vallen = vp->vallen;
+       ep->tstamp = vp->tstamp;
+       ep->fstamp = vp->fstamp;
+       ep->vallen = vp->vallen;
        len = 12;
        temp32 = ntohl(vp->vallen);
        if (temp32 == 0 || vp->ptr == NULL)
                return (len);
-       memcpy(&pp->pkt[0], vp->ptr, temp32);
+       memcpy(ep->pkt, vp->ptr, temp32);
 
        /*
         * Copy signature. If the signature field is empty or zero
-        * length, return an empty  signature with field length of zero.
+        * length, return an empty signature with field length of zero.
         */
        i = (temp32 + 3) / 4;
        len += i * 4 + 4;
-       pp->pkt[i++] = vp->siglen;
+       ep->pkt[i++] = vp->siglen;
        temp32 = ntohl(vp->siglen);
        if (temp32 == 0 || vp->sig == NULL)
                return (len);
-       memcpy(&pp->pkt[i], vp->sig, temp32);
+       memcpy(&ep->pkt[i], vp->sig, temp32);
        len += temp32;
        return (len);
 }
 
 
 /*
- * crypto_sign - compute new public value and sign extension fields.
- * Note that the signature length will be zero if something breaks in
- * here.
- */
-void
-crypto_sign(void)
-{
-       EVP_MD_CTX ctx;         /* signature context */
+ * crypto_update - compute new public value and sign extension fields
+ *
+ * This routine runs periodically, like once a day, and when something
+ * changes. It updates the timestamps on three value structures and one
+ * value structure list, then signs all the structures:
+ *
+ * hostval     host name (not signed)
+ * pubkey      public key
+ * cinfo       certificate info/value list
+ * tai_leap    leapseconds file
+ *
+ * Filestamps are proventicated data, so this routine is run only when
+ * the host has been synchronized to a proventicated source. Thus, the
+ * timestamp is proventicated, too, and can be used to deflect
+ * clogging attacks and even cook breakfast.
+ *
+ * Returns void (no errors)
+ */
+void
+crypto_update(void)
+{
+       EVP_MD_CTX ctx;         /* message digest context */
+       struct cert_info *cp;   /* certificate info/value structure */
        u_char  statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
-       l_fp    tstamp;         /* NTP timestamp */
+       tstamp_t tstamp;        /* NTP timestamp */
        u_int   len;
 
-       /*
-        * This routine runs periodically, like once a day, and when a
-        * filestamp changes. It updates the autokey private value and
-        * the timestamps on four value structures:
-        *
-        * host         public/private key file
-        * pubkey       public key
-        * cinfo        certificate file
-        * tai_leap     ERTS leapseconds file
-        *
-        * Filestamps are proventicated data, so this routine is run
-        * only when the host has been synchronized to a proventicated
-        * source(s). Thus, the timestamp is proventicated, too, and can
-        * be used to deflect clogging attacks and cook breakfast.
-        */
-       if (sys_leap == LEAP_NOTINSYNC)
+       if ((tstamp = crypto_time()) == 0)
                return;
-       get_systime(&tstamp);
-       host.tstamp = htonl(tstamp.l_ui);
-
-       /*
-        * Sign certificate and timestamps. The filestamp is derived
-        * from the certificate file extension from wherever the file
-        * was generated.
-        */
-       if (cinfo.vallen != 0) {
-               cinfo.tstamp = host.tstamp;
-               cinfo.siglen = 0;
-               if (cinfo.sig == NULL)
-                       cinfo.sig = emalloc(sign_siglen);
-               EVP_SignInit(&ctx, digest);
-               EVP_SignUpdate(&ctx, (u_char *)&cinfo, 12);
-               EVP_SignUpdate(&ctx, cinfo.ptr, ntohl(cinfo.vallen));
-               if (EVP_SignFinal(&ctx, cinfo.sig, &len, sign_pkey))
-                       cinfo.siglen = htonl(len);
-       }
+       hostval.tstamp = htonl(tstamp);
 
        /*
         * Sign public key and timestamps. The filestamp is derived from
-        * the certicate filestamp.
+        * the host key file extension from wherever the file was
+        * generated. 
         */
        if (pubkey.vallen != 0) {
-               pubkey.tstamp = host.tstamp;
+               pubkey.tstamp = hostval.tstamp;
                pubkey.siglen = 0;
                if (pubkey.sig == NULL)
                        pubkey.sig = emalloc(sign_siglen);
-               EVP_SignInit(&ctx, digest);
+               EVP_SignInit(&ctx, sign_digest);
                EVP_SignUpdate(&ctx, (u_char *)&pubkey, 12);
                EVP_SignUpdate(&ctx, pubkey.ptr, ntohl(pubkey.vallen));
                if (EVP_SignFinal(&ctx, pubkey.sig, &len, sign_pkey))
                        pubkey.siglen = htonl(len);
        }
 
+       /*
+        * Sign certificates and timestamps. The filestamp is derived
+        * from the certificate file extension from wherever the file
+        * was generated.
+        */
+       for (cp = cinfo; cp != NULL; cp = cp->link) {
+               cp->cert.tstamp = hostval.tstamp;
+               cp->cert.siglen = 0;
+               if (cp->cert.sig == NULL)
+                       cp->cert.sig = emalloc(sign_siglen);
+               EVP_SignInit(&ctx, sign_digest);
+               EVP_SignUpdate(&ctx, (u_char *)&cp->cert, 12);
+               EVP_SignUpdate(&ctx, cp->cert.ptr,
+                   ntohl(cp->cert.vallen));
+               if (EVP_SignFinal(&ctx, cp->cert.sig, &len, sign_pkey))
+                       cp->cert.siglen = htonl(len);
+       }
+
        /*
         * Sign leapseconds table and timestamps. The filestamp is
         * derived from the leapsecond file extension from wherever the
         * file was generated.
         */
        if (tai_leap.vallen != 0) {
-               tai_leap.tstamp = host.tstamp;
+               tai_leap.tstamp = hostval.tstamp;
                tai_leap.siglen = 0;
                if (tai_leap.sig == NULL)
                        tai_leap.sig = emalloc(sign_siglen);
-               EVP_SignInit(&ctx, digest);
+               EVP_SignInit(&ctx, sign_digest);
                EVP_SignUpdate(&ctx, (u_char *)&tai_leap, 12);
                EVP_SignUpdate(&ctx, tai_leap.ptr,
                    ntohl(tai_leap.vallen));
                if (EVP_SignFinal(&ctx, tai_leap.sig, &len, sign_pkey))
                        tai_leap.siglen = htonl(len);
        }
-       sprintf(statstr, "sign ts %u", ntohl(host.tstamp)); 
+       sprintf(statstr, "sign ts %u", ntohl(hostval.tstamp)); 
        record_crypto_stats(NULL, statstr);
 #ifdef DEBUG
        if (debug)
@@ -1233,12 +1415,491 @@ crypto_sign(void)
 
 
 /*
- * crypto_key - load key. Used for both host key and sign key.
+ * value_free - free value structure components.
+ *
+ * Returns void (no errors)
+ */
+void
+value_free(
+       struct value *vp        /* value structure */
+       )
+{
+       if (vp->ptr != NULL)
+               free(vp->ptr);
+       if (vp->sig != NULL)
+               free(vp->sig);
+       memset(vp, 0, sizeof(struct value));
+}
+
+
+/*
+ * crypto_time - returns current NTP time in seconds.
+ */
+tstamp_t
+crypto_time()
+{
+       l_fp    tstamp;         /* NTP time */  L_CLR(&tstamp);
+
+       L_CLR(&tstamp);
+       if (sys_leap != LEAP_NOTINSYNC)
+               get_systime(&tstamp);
+       return (tstamp.l_ui);
+}
+
+
+/*
+ * asn2ntp - convert ASN1_TIME time structure to NTP time in seconds.
+ */
+u_long
+asn2ntp        (
+       ASN1_TIME *asn1time     /* pointer to ASN1_TIME structure */
+       )
+{
+       char    *v;             /* pointer to ASN1_TIME string */
+       struct  tm tm;          /* used to convert to NTP time */
+
+       /*
+        * Extract time string YYMMDDHHMMSSZ from ASN1 time structure.
+        * Note that the YY, MM, DD fields start with one, the HH, MM,
+        * SS fiels start with zero and the Z character should be 'Z'
+        * for UTC. Also note that years less than 50 map to years
+        * greater than 100. Dontcha love ASN.1? Better than MIL-188.
+        */
+       if (asn1time->length > 13)
+               return (-1);
+       v = (char *)asn1time->data;
+       tm.tm_year = (v[0] - '0') * 10 + v[1] - '0';
+       if (tm.tm_year < 50)
+               tm.tm_year += 100;
+       tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1;
+       tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0';
+       tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0';
+       tm.tm_min = (v[8] - '0') * 10 + v[9] - '0';
+       tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0';
+       tm.tm_wday = 0;
+       tm.tm_yday = 0;
+       tm.tm_isdst = 0;
+       return (mktime(&tm) + JAN_1970);
+}
+
+
+/*
+ ***********************************************************************
+ *                                                                    *
+ * The following routines are used to manipulate certificates          *
+ *                                                                    *
+ ***********************************************************************
+ */
+/*
+ * cert_parse - parse x509 certificate and create info/value structures.
+ *
+ * The server certificate includes the version number, issuer name,
+ * subject name, public key and valid date interval. If the issuer name
+ * is the same as the subject name, the certificate is self-signed and
+ * valid only if the server is configured as trustable. If the names are
+ * different, another issuer has signed the server certificate and
+ * vouched for it. In this case the server certificate is valid if
+ * verified by the issuer public key.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
+ */
+struct cert_info *             /* certificate information structure */
+cert_parse(
+       u_char  *asn1cert,      /* X509 certificate */
+       u_int   len,            /* certificate length */
+       tstamp_t fstamp         /* filestamp */
+       )
+{
+       X509    *cert;          /* X509 certificate */
+       struct cert_info *ret;  /* certificate info/value */
+       u_char  pathbuf[MAXFILENAME];
+       u_char  *ptr;
+
+       /*
+        * Decode ASN.1 objects and construct certificate structure.
+        */
+       ptr = asn1cert;
+       if ((cert = d2i_X509(NULL, &ptr, len)) == NULL) {
+               msyslog(LOG_ERR, "cert_parse %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+               return (NULL);
+       }
+
+       /*
+        * Extract version, subject name and public key.
+        */
+       ret = emalloc(sizeof(struct cert_info));
+       memset(ret, 0, sizeof(struct cert_info));
+       if ((ret->pkey = X509_get_pubkey(cert)) == NULL) {
+               msyslog(LOG_ERR, "cert_parse %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+               cert_free(ret);
+               X509_free(cert);
+               return (NULL);
+       }
+       ret->cert_version = X509_get_version(cert);
+       X509_NAME_oneline(X509_get_subject_name(cert), pathbuf,
+           MAXFILENAME - 1);
+       if ((ptr = strchr(pathbuf, '=')) == NULL) {
+               msyslog(LOG_ERR, "cert_parse: invalid subject %s",
+                   pathbuf);
+               cert_free(ret);
+               X509_free(cert);
+               return (NULL);
+       }
+       ret->subject = emalloc(strlen(++ptr) + 1);
+       strcpy(ret->subject, ptr);
+
+       /*
+        * Extract remaining objects. Note that the NTP serial number is
+        * the NTP seconds at the time of signing, but this might not be
+        * the case for other authority. We don't bother to check the
+        * objects at this time, since the real crunch can happen only
+        * when the time is valid but not yet certificated.
+        */
+       ret->nid = OBJ_obj2nid(cert->cert_info->signature->algorithm);
+       ret->digest = (EVP_MD *)EVP_get_digestbynid(ret->nid);
+       ret->serial =
+           (u_long)ASN1_INTEGER_get(X509_get_serialNumber(cert));
+       X509_NAME_oneline(X509_get_issuer_name(cert), pathbuf,
+           MAXFILENAME);
+       if ((ptr = strchr(pathbuf, '=')) == NULL) {
+               msyslog(LOG_ERR, "cert_parse: invalid issuer %s",
+                   pathbuf);
+               cert_free(ret);
+               X509_free(cert);
+               return (NULL);
+       }
+       ret->issuer = emalloc(strlen(++ptr) + 1);
+       strcpy(ret->issuer, ptr);
+       ret->first = asn2ntp(X509_get_notBefore(cert));
+       ret->last = asn2ntp(X509_get_notAfter(cert));
+
+       /*
+        * If certificate is self-signed, verify signature.
+        */
+       if (strcmp(ret->subject, ret->issuer) == 0) {
+               if (!X509_verify(cert, ret->pkey)) {
+                       msyslog(LOG_ERR,
+                           "cert_parse: signature not verified %s",
+                           pathbuf);
+                       cert_free(ret);
+                       X509_free(cert);
+                       return (NULL);
+               }
+       }
+
+       /*
+        * Verify certificate valid times. Note that certificates cannot
+        * be retroactive.
+        */
+       if (ret->first > ret->last || ret->first < fstamp) {
+               msyslog(LOG_ERR,
+                   "certificate signature not valid %s",
+                   ret->subject);
+               cert_free(ret);
+               X509_free(cert);
+               return (NULL);
+       }
+
+       /*
+        * Build the value structure to sign and send later.
+        */
+       ret->cert.fstamp = htonl(fstamp);
+       ret->cert.vallen = htonl(len);
+       ret->cert.ptr = emalloc(len);
+       memcpy(ret->cert.ptr, asn1cert, len);
+#ifdef DEBUG
+       if (debug > 1)
+               X509_print_fp(stdout, cert);
+#endif
+       X509_free(cert);
+       return (ret);
+}
+
+
+/*
+ * cert_sign - sign x509 certificate and update value structure.
+ *
+ * The certificate request is a copy of the client certificate, which
+ * includes the version number, subject name and public key of the
+ * client. The resulting certificate includes these values plus the
+ * serial number, issuer name and validity interval of the server. The
+ * validity interval extends from the current time to the same time one
+ * year hence. For NTP purposes, it is convenient to use the NTP seconds
+ * of the current time as the serial number.
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_CRT   bad or missing certificate
+ * XEVNT_SIG   signature not verified
+ */
+static int
+cert_sign(
+       struct exten *ep,       /* extension field pointer */
+       struct value *vp        /* value pointer */
+       )
+{
+       X509    *req;           /* X509 certificate request */
+       X509    *cert;          /* X509 certificate */
+       ASN1_INTEGER *serial;   /* serial number */
+       X509_NAME *subj;        /* distinguished (common) name */
+       EVP_PKEY *pkey;         /* public key */
+       EVP_MD_CTX ctx;         /* message digest context */
+       tstamp_t tstamp;        /* NTP timestamp */
+       u_int   len;
+       u_char  *ptr;
+
+       /*
+        * Decode ASN.1 objects and construct certificate structure.
+        */
+       tstamp = crypto_time();
+       ptr = (u_char *)ep->pkt;
+       if ((req = d2i_X509(NULL, &ptr, ntohl(ep->vallen))) == NULL) {
+               msyslog(LOG_ERR, "cert_sign %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+               return (XEVNT_CRT);
+       }
+       /*
+        * Extract public key and check for errors.
+        */
+       if ((pkey = X509_get_pubkey(req)) == NULL) {
+               msyslog(LOG_ERR, "cert_sign %s",
+                   ERR_error_string(ERR_get_error(), NULL));
+               X509_free(req);
+               return (XEVNT_PUB);
+       }
+
+       /*
+        * Generate X509 certificate signed by this server. For this
+        * prupose the issuer name is the server name.
+        */
+       cert = X509_new();
+       X509_set_version(cert, X509_get_version(req));
+       serial = ASN1_INTEGER_new();
+       ASN1_INTEGER_set(serial, tstamp);
+       X509_set_serialNumber(cert, serial);
+       X509_gmtime_adj(X509_get_notBefore(cert), 0L);
+       X509_gmtime_adj(X509_get_notAfter(cert), YEAR);
+       subj = X509_get_issuer_name(cert);
+       X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
+           sys_hostname, strlen(sys_hostname), -1, 0);
+       subj = X509_get_subject_name(req);
+       X509_set_subject_name(cert, subj);
+       X509_set_pubkey(cert, pkey);
+       X509_free(req);
+
+       /*
+        * Sign and verify the certificate.
+        */
+       X509_sign(cert, sign_pkey, sign_digest);
+       if (!X509_verify(cert, sign_pkey)) {
+               printf("Verify certificate fails\n%s\n",
+                   ERR_error_string(ERR_get_error(), NULL));
+               X509_free(cert);
+               return (XEVNT_SIG);
+       }
+       len = i2d_X509(cert, NULL);
+
+       /*
+        * Build and sign the value structure. We have to sign it here,
+        * since the response has to be returned right away. This is a
+        * clogging hazard.
+        */
+       memset(vp, 0, sizeof(struct value));
+       vp->tstamp = htonl(tstamp);
+       vp->fstamp = hostval.tstamp;
+       vp->vallen = htonl(len);
+       vp->ptr = emalloc(len);
+       ptr = vp->ptr;
+       i2d_X509(cert, &ptr);
+       vp->siglen = 0;
+       vp->sig = emalloc(sign_siglen);
+       EVP_SignInit(&ctx, sign_digest);
+       EVP_SignUpdate(&ctx, (u_char *)vp, 12);
+       EVP_SignUpdate(&ctx, vp->ptr, len);
+       if (EVP_SignFinal(&ctx, vp->sig, &len, sign_pkey))
+               vp->siglen = htonl(len);
+#ifdef DEBUG
+       if (debug > 1)
+               X509_print_fp(stdout, cert);
+#endif
+       X509_free(cert);
+       return (XEVNT_OK);
+}
+
+
+/*
+ * cert_valid - verify certificate with given public key
+ *
+ * This is pretty ugly, as the certificate has to be verified in the
+ * OpenSSL X509 structure, not in the DER format in the info/value
+ * structure.
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_SIG   signature not verified
+ */
+int
+cert_valid(
+       struct cert_info *cinf, /* certificate information structure */
+       EVP_PKEY *pkey          /* public key */
+       )
+{
+       X509    *cert;          /* X509 certificate */
+       u_char  *ptr;
+
+       if (cinf->flags & CERT_VALID)
+               return (XEVNT_OK);
+       ptr = (u_char *)cinf->cert.ptr;
+       cert = d2i_X509(NULL, &ptr, ntohl(cinf->cert.vallen));
+       if (!X509_verify(cert, pkey))
+               return (XEVNT_SIG);
+       cinf->flags |= CERT_VALID;
+       X509_free(cert);
+       return (XEVNT_OK);
+}
+
+
+/*
+ * cert - install certificate in certificate list
+ *
+ * This routine encodes an extension field into a certificate info/value
+ * structure. It searches the certificate list for duplicates and
+ * expunges whichever is older. It then searches the list for other
+ * certificates that might be verified by latest one. Finally, it
+ * appends the latest certificate to the beginning of the list.
+ *
+ * Returns
+ * XEVNT_OK    success
+ * XEVNT_SIG   signature not verified
+ * XEVNT_PER   certificate expired
+ * XEVNT_CRT   bad or missing certificate 
+ */
+int
+cert_install (
+       struct exten *ep,       /* cert info/value */
+       struct peer *peer       /* peer structure */
+       )
+{
+       struct cert_info *cp, *xp, *yp, **zp;
+       int     rval;
+       tstamp_t tstamp;
+
+       /*
+        * Parse and validate the signed certificate. If valid,
+        * construct the info/value structure; otherwise, scamper home.
+        */
+       if ((cp = cert_parse((u_char *)ep->pkt, ntohl(ep->vallen),
+           ntohl(ep->fstamp))) == NULL)
+               return (XEVNT_CRT);
+
+       tstamp = ntohl(ep->tstamp);
+       if (tstamp < cp->first || tstamp > cp->last) {
+               cert_free(cp);
+               return (XEVNT_PER);
+       }
+
+       /*
+        * Scan certificate list looking for another certificate with
+        * the same subject and issuer. If another is found with the
+        * same or older filestamp, unlink it and return the goodies to
+        * the heap. If another is found with a later filetsamp, discard
+        * the given one. 
+        */
+       yp = cp;
+       zp = &cinfo;
+       for (xp = cinfo; xp != NULL; xp = xp->link) {
+               if (strcmp(cp->subject, xp->subject) == 0 &&
+                   strcmp(cp->issuer, xp->issuer) == 0) {
+                       *zp = xp->link;;
+                       if (cp->cert.fstamp <= xp->cert.fstamp) {
+                               cert_free(xp);
+                       } else {
+                               cert_free(cp);
+                               yp = xp;
+                       }
+                       break;
+               }
+               zp = &xp->link;
+       }
+
+       /*
+        * Scan the certificate list looking for certificates to
+        * validate. If the subject name of this certificate S matches
+        * the issuer name of of another certificate X and the signature
+        * of X is valid when using the S public key, then X is valid.
+        *
+        * If the subject name of X matches the host name and the
+        * certificate is valid, set the proventic bit.
+
+        */
+       rval = XEVNT_OK;
+       for (xp = cinfo; xp != NULL; xp = xp->link) {
+               if (strcmp(xp->issuer, yp->subject) != 0)
+                       continue;
+
+               rval = cert_valid(xp, yp->pkey);
+               if (rval != XEVNT_OK)
+                       break;
+
+               xp->flags |= CERT_VALID;
+               if (strcmp(xp->subject, peer->subject) == 0)
+                       peer->crypto |= CRYPTO_FLAG_PROV;
+               else if (strcmp(xp->subject, sys_hostname) == 0)
+                       peer->crypto |= CRYPTO_FLAG_VRFY;
+       }
+
+       /*
+        * That was heavy. If we survived this far, the new certificate
+        * goes first on the list.
+        */
+       yp->link = cinfo;
+       cinfo = yp;
+       crypto_update();
+       return (rval);
+}
+
+
+/*
+ * cert_free - free certificate information structure
+ */
+void
+cert_free(
+       struct cert_info *cinf  /* certificate info/value structure */ 
+       )
+{
+       if (cinf->pkey != NULL)
+               EVP_PKEY_free(cinf->pkey);
+       if (cinf->subject != NULL)
+               free(cinf->subject);
+       if (cinf->issuer != NULL)
+               free(cinf->issuer);
+       value_free(&cinf->cert);
+       free(cinf);
+}
+
+
+/*
+ ***********************************************************************
+ *                                                                    *
+ * The following routines are used only at initialization time         *
+ *                                                                    *
+ ***********************************************************************
+ */
+/*
+ * crypto_key - load host and sign keys from file
+ *
+ * This routine loads a PEM-encoded public/private key pair and extracts
+ * the filestamp from the file name.
+ *
+ * Returns public key pointer if valid, NULL if not. Side effect updates
+ * the filestamp if valid.
  */
 static EVP_PKEY *
 crypto_key(
        char    *cp,            /* file name */
-       struct value *vp        /* value structure pointer */
+       tstamp_t *fstamp        /* filestamp */
        )
 {
        FILE    *str;           /* file handle */
@@ -1246,7 +1907,6 @@ crypto_key(
        char    filename[MAXFILENAME]; /* name of rsa key file */
        char    linkname[MAXFILENAME]; /* file link (for filestamp) */
        u_char  statstr[NTP_MAXSTRLEN]; /* statistics for filegen */
-       tstamp_t fstamp;                /* filestamp */
        int     rval;
        u_char  *ptr;
 
@@ -1285,11 +1945,10 @@ crypto_key(
                ptr = strrchr(filename, '.');
        }
        if (ptr != NULL)
-               sscanf(++ptr, "%u", &fstamp);
+               sscanf(++ptr, "%u", fstamp);
        else
-               fstamp = 0;
-       vp->fstamp = htonl(fstamp);
-       sprintf(statstr, "%s link %d fs %u mod %d", cp, rval, fstamp,
+               *fstamp = 0;
+       sprintf(statstr, "%s link %d fs %u mod %d", cp, rval, *fstamp,
            EVP_PKEY_size(pkey) * 8);
        record_crypto_stats(NULL, statstr);
 #ifdef DEBUG
@@ -1307,7 +1966,14 @@ crypto_key(
 
 
 /*
- * crypto_cert - load X.509 RSA or DSA certificate.
+ * crypto_cert - load certificate from file
+ *
+ * This routine loads a X.509 RSA or DSA certificate from a file and
+ * constructs a info/cert value structure for this machine. The
+ * structure includes a filestamp extracted from the file name. Later
+ * the certificate can be sent to another machine by request.
+ *
+ * Returns certificate info/value pointer if valid, NULL if not.
  */
 static struct cert_info *      /* certificate information */
 crypto_cert(
@@ -1357,18 +2023,6 @@ crypto_cert(
        }
        free(name);
 
-       /*
-        * Parse certificate for errors.
-        */
-       ret = cert_parse(data, len);
-       if (ret == NULL) {
-               msyslog(LOG_ERR, "crypto_cert invalid certificate");
-               free(data);
-               return (NULL);
-       }
-       ret->cert = data;
-       ret->cert_len = len;
-
        /*
         * Extract filestamp if present.
         */
@@ -1383,7 +2037,16 @@ crypto_cert(
                sscanf(++ptr, "%u", &fstamp);
        else
                fstamp = 0;
-       ret->fstamp = fstamp;
+
+       /*
+        * Parse certificate and generate info/value structure.
+        */
+       ret = cert_parse(data, len, fstamp);
+       free(data);
+       if (ret == NULL) {
+               msyslog(LOG_ERR, "crypto_cert invalid certificate");
+               return (NULL);
+       }
        sprintf(statstr, "%s link %d fs %u len %u", cp, rval, fstamp,
            len);
        record_crypto_stats(NULL, statstr);
@@ -1396,7 +2059,13 @@ crypto_cert(
 
 
 /*
- * crypto_tai - load leapseconds table
+ * crypto_tai - load leapseconds table from file
+ *
+ * This routine loads the ERTS leapsecond file in NIST text format,
+ * converts to a value structure and extracts a filestamp from the file
+ * name. The data are used to establish the TAI offset from UTC, which
+ * is provided to the kernel if supported. Later the data can be sent to
+ * another machine on request.
  */
 static void
 crypto_tai(
@@ -1434,6 +2103,22 @@ crypto_tai(
        if ((str = fopen(filename, "r")) == NULL)
                return;
 
+       /*
+        * Extract filestamp if present.
+        */
+       rval = readlink(filename, linkname, MAXFILENAME - 1);
+       if (rval > 0) {
+               linkname[rval] = '\0';
+               ptr = strrchr(linkname, '.');
+       } else {
+               ptr = strrchr(filename, '.');
+       }
+       if (ptr != NULL)
+               sscanf(++ptr, "%u", &fstamp);
+       else
+               fstamp = 0;
+       tai_leap.fstamp = htonl(fstamp);
+
        /*
         * We are rather paranoid here, since an intruder might cause a
         * coredump by infiltrating naughty values. Empty lines and
@@ -1445,7 +2130,6 @@ crypto_tai(
         * 1972 plus one second for each succeeding insertion.
         */
        i = 0;
-       rval = XEVNT_OK;
        while (i < MAX_LEAP) {
                ptr = fgets(buf, NTP_MAXSTRLEN - 1, str);
                if (ptr == NULL)
@@ -1457,13 +2141,12 @@ crypto_tai(
                if (sscanf(buf, "%u %u", &leapsec[i], &offset) != 2)
                        continue;
                if (i != offset - TAI_1972) { 
-                       rval = XEVNT_DAT;
                        break;
                }
                i++;
        }
        fclose(str);
-       if (rval != XEVNT_OK || i == 0) {
+       if (ptr != NULL) {
                msyslog(LOG_ERR,
                    "crypto_tai leapseconds file %s error %d", cp,
                    rval);
@@ -1492,22 +2175,6 @@ crypto_tai(
                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';
-               ptr = strrchr(linkname, '.');
-       } else {
-               ptr = strrchr(filename, '.');
-       }
-       if (ptr != NULL)
-               sscanf(++ptr, "%u", &fstamp);
-       else
-               fstamp = 0;
-       tai_leap.fstamp = htonl(fstamp);
        sprintf(statstr, "%s link %d fs %u offset %u", cp, rval, fstamp,
            ntohl(tai_leap.vallen) / 4 + TAI_1972 - 1);
        record_crypto_stats(NULL, statstr);
@@ -1517,163 +2184,21 @@ crypto_tai(
 #endif
 }
 
-
-/*
- * cert_parse - parse x509 certificate and create information structure.
- */
-struct cert_info *             /* certificate information structure */
-cert_parse(
-       u_char  *asn1cert,      /* X509 certificate */
-       u_int   len             /* certificate length */
-       )
-{
-       struct cert_info *ret;
-       u_char  pathbuf[MAXFILENAME];
-       X509    *cert;
-
-       /*
-        * Decode ASN.1 objects and construct certificate structure.
-        */
-       if ((cert = d2i_X509(NULL, &asn1cert, len)) == NULL) {
-               msyslog(LOG_ERR, "cert_parse %s",
-                   ERR_error_string(ERR_get_error(), NULL));
-               return (NULL);
-       }
-
-       /*
-        * Extract public key, signature and message digest algorithms.
-        */
-       ret = emalloc(sizeof(struct cert_info));
-       memset(ret, 0, sizeof(struct cert_info));
-       if ((ret->pkey = X509_get_pubkey(cert)) == NULL) {
-               msyslog(LOG_ERR, "cert_parse %s",
-                   ERR_error_string(ERR_get_error(), NULL));
-               cert_free(ret);
-               return (NULL);
-       }
-
-       /*
-        * Extract remaining objects. Note that the NTP serial number is
-        * the NTP seconds at the time of signing, but this might not be
-        * the case for other authority. We don't bother to check the
-        * objects at this time, since the real crunch can happen only
-        * when the time is valid but not yet certificated.
-        */
-       ret->cert_version = X509_get_version(cert);
-       ret->nid = OBJ_obj2nid(cert->cert_info->signature->algorithm);
-       ret->serial =
-           (u_long)ASN1_INTEGER_get(X509_get_serialNumber(cert));
-       X509_NAME_oneline(X509_get_subject_name(cert), pathbuf,
-           MAXFILENAME);
-       ret->subject = emalloc(strlen(pathbuf) - 3);
-       strcpy(ret->subject, &pathbuf[4]);
-       X509_NAME_oneline(X509_get_issuer_name(cert), pathbuf,
-           MAXFILENAME);
-       ret->issuer = emalloc(strlen(pathbuf) - 3);
-       strcpy(ret->issuer, &pathbuf[4]);
-       ret->first = asn2ntp(X509_get_notBefore(cert));
-       ret->last = asn2ntp(X509_get_notAfter(cert));
-
-       /*
-        * Verify self-signed certificate. This goes away someday.
-        */
-       if (strcmp(ret->subject, ret->issuer) == 0 && !X509_verify(cert,
-           ret->pkey)) {
-               msyslog(LOG_ERR, "signature not verified %s",
-                   ret->subject);
-               cert_free(ret);
-               return (NULL);
-       }
-#if DEBUG
-       if (debug > 1)
-               X509_print_fp(stdout, cert);
-#endif
-       X509_free(cert);
-       return (ret);
-}
-
-
-/*
- * cert_free - free certificate information structure.
- */
-void
-cert_free(
-       struct cert_info *cinf  /* certificate information structure */ 
-       )
-{
-       if (cinf->pkey != NULL)
-               EVP_PKEY_free(cinf->pkey);
-       if (cinf->subject != NULL)
-               free(cinf->subject);
-       if (cinf->issuer != NULL)
-               free(cinf->issuer);
-       if (cinf->cert != NULL)
-               free(cinf->cert);
-       free(cinf);
-}
-
-/*
- * value_free - free value structure components.
- */
-void
-value_free(
-       struct value *vp        /* value structure */
-       )
-{
-       if (vp->ptr != NULL)
-               free(vp->ptr);
-       if (vp->sig != NULL)
-               free(vp->sig);
-       memset(vp, 0, sizeof(struct value));
-}
-
-
-/*
- * asn2ntp - convert ASN1_TIME time structure to NTP time
- */
-u_long
-asn2ntp        (
-       ASN1_TIME *asn1time     /* pointer to ASN1_TIME structure */
-       )
-{
-       char    *v;             /* pointer to ASN1_TIME string */
-       struct  tm tm;          /* used to convert to NTP time */
-
-       /*
-        * Extract time string YYMMDDHHMMSSZ from ASN1 time structure.
-        * Note that the YY, MM, DD fields start with one, the HH, MM,
-        * SS fiels start with zero and the Z character should be 'Z'
-        * for UTC. Also note that years less than 50 map to years
-        * greater than 100. Dontcha love ASN.1? Better than MIL-188.
-        */
-       if (asn1time->length > 13)
-               return (-1);
-       v = (char *)asn1time->data;
-       tm.tm_year = (v[0] - '0') * 10 + v[1] - '0';
-       if (tm.tm_year < 50)
-               tm.tm_year += 100;
-       tm.tm_mon = (v[2] - '0') * 10 + v[3] - '0' - 1;
-       tm.tm_mday = (v[4] - '0') * 10 + v[5] - '0';
-       tm.tm_hour = (v[6] - '0') * 10 + v[7] - '0';
-       tm.tm_min = (v[8] - '0') * 10 + v[9] - '0';
-       tm.tm_sec = (v[10] - '0') * 10 + v[11] - '0';
-       tm.tm_wday = 0;
-       tm.tm_yday = 0;
-       tm.tm_isdst = 0;
-       return (mktime(&tm) + JAN_1970);
-}
-
-
 /*
- * crypto_setup - load required rsa key, certificate and optional
- * leapseconds table. Initialize extension fields for later signatures.
+ * crypto_setup - load keys, certificate and leapseconds table
+ *
+ * This routine loads the public/private host key and certificate. If
+ * available, it loads the public/private sign key, which defaults to
+ * the host key, and leapseconds table. The host key must be RSA, but
+ * the sign key can be either RSA or DSA. In either case, the public key
+ * on the certificate must agree with the sign key.
  */
 void
 crypto_setup(void)
 {
-       struct cert_info *cinf; /* certificate information */
        char    filename[MAXFILENAME]; /* name of rsa key file */
-       l_fp    tstamp;         /* NTP timestamp */
+       l_fp    seed;           /* crypto PRNG seed as NTP timestamp */
+       tstamp_t fstamp;        /* filestamp */
        u_int   len, bytes;
        u_char  *ptr;
 
@@ -1682,14 +2207,12 @@ crypto_setup(void)
         */
        if (!crypto_flags)
                return;
-
        gethostname(filename, MAXFILENAME);
        bytes = strlen(filename) + 1;
        sys_hostname = emalloc(bytes);
        memcpy(sys_hostname, filename, bytes);
-       memset(&host, 0, sizeof(host));
+       memset(&hostval, 0, sizeof(hostval));
        memset(&pubkey, 0, sizeof(pubkey));
-       memset(&cinfo, 0, sizeof(cinfo));
        memset(&tai_leap, 0, sizeof(tai_leap));
 
        /*
@@ -1720,18 +2243,19 @@ crypto_setup(void)
                    ERR_error_string(ERR_get_error(), NULL));
                exit (-1);
        }
-       get_systime(&tstamp);
-       RAND_seed(&tstamp, sizeof(tstamp));
+       get_systime(&seed);
+       RAND_seed(&seed, sizeof(l_fp));
        RAND_write_file(rand_file);
        OpenSSL_add_all_algorithms();
 #ifdef DEBUG
        if (debug)
-               printf("crypto_setup: %d bytes read from random seed file %s\n",
+               printf(
+                   "crypto_setup: %d bytes read from seed file %s\n",
                    bytes, rand_file);
 #endif
 
        /*
-        * Load required rsa key, default "ntpkey_key_<hostname>".
+        * Load required host key, default "ntpkey_key_<hostname>".
         */
        if (rsa_file == NULL) {
                snprintf(filename, MAXFILENAME, "ntpkey_key_%s",
@@ -1739,19 +2263,20 @@ crypto_setup(void)
                rsa_file = emalloc(strlen(filename) + 1);
                strcpy(rsa_file, filename);
        }
-       if ((rsa_pkey = crypto_key(rsa_file, &host)) == NULL) {
+       if ((rsa_pkey = crypto_key(rsa_file, &fstamp)) == NULL) {
                msyslog(LOG_ERR,
-                   "rsa key file %s not found or corrupt",
+                   "host key file %s not found or corrupt",
                    rsa_file);
                exit (-1);
        }
+       hostval.fstamp = htonl(fstamp);
        if (EVP_MD_type(rsa_pkey) != EVP_PKEY_RSA) {
                msyslog(LOG_ERR,
                    "rsa key file %s wrong type", rsa_file);
                exit (-1);
        }
-       host.vallen = htonl(strlen(sys_hostname));
-       host.ptr = sys_hostname;
+       hostval.vallen = htonl(strlen(sys_hostname));
+       hostval.ptr = sys_hostname;
        crypto_flags |= CRYPTO_FLAG_KEY;
        
        /*
@@ -1763,10 +2288,11 @@ crypto_setup(void)
        pubkey.ptr = ptr;
        i2d_PublicKey(rsa_pkey, &ptr);
        pubkey.vallen = htonl(len);
-       pubkey.fstamp = htonl(host.fstamp);
+       pubkey.fstamp = htonl(hostval.fstamp);
 
        /*
-        * Load optional sign key, default "ntpkey_sign_<hostname>".
+        * Load optional sign key, default "ntpkey_sign_<hostname>". If
+        * not found, use the host key..
         */
        if (sign_file == NULL) {
                snprintf(filename, MAXFILENAME, "ntpkey_sign_%s",
@@ -1774,16 +2300,15 @@ crypto_setup(void)
                sign_file = emalloc(strlen(filename) + 1);
                strcpy(sign_file, filename);
        }
-       if ((sign_pkey = crypto_key(sign_file, &cinfo)) != NULL) {
+       if ((sign_pkey = crypto_key(sign_file, &fstamp)) != NULL)
                crypto_flags |= CRYPTO_FLAG_SIGN;
-       } else {
+       else
                sign_pkey = rsa_pkey;
-               cinfo.fstamp = host.fstamp;
-       }
        sign_siglen = EVP_PKEY_size(sign_pkey);
 
        /*
-        * Load required certificate, default "ntpkey_cert_<hostname>".
+        * Load required self-signed certificate, default
+        * "ntpkey_cert_<hostname>".
         */
        if (cert_file == NULL) {
                snprintf(filename, MAXFILENAME, "ntpkey_cert_%s",
@@ -1791,25 +2316,14 @@ crypto_setup(void)
                cert_file = emalloc(strlen(filename) + 1);
                strcpy(cert_file, filename);
        }
-       if ((cinf = crypto_cert(cert_file)) == NULL) {
+       if ((cinfo = crypto_cert(cert_file)) == NULL) {
                msyslog(LOG_ERR,
                    "certificate file %s not found or corrupt",
                    cert_file);
                exit (-1);
        }
-       cinfo.ptr = emalloc(cinf->cert_len);
-       memcpy(cinfo.ptr, cinf->cert, cinf->cert_len);
-       cinfo.vallen = htonl(cinf->cert_len);
-       if (cinf->fstamp != htonl(cinfo.fstamp)) {
-               msyslog(LOG_ERR,
-                   "certificate file %s does not match sign key file",
-                   cert_file);
-               exit (-1);
-       }
-       cinfo.fstamp = htonl(cinf->fstamp);
-       crypto_flags |= cinf->nid << 16;
-       digest = EVP_get_digestbynid(cinf->nid);
-       cert_free(cinf);
+       sign_digest = cinfo->digest;
+       crypto_flags |= cinfo->nid << 16;
 
        /*
         * Load optional leapseconds table, default "ntpkey_leap". If
@@ -1822,14 +2336,13 @@ crypto_setup(void)
 #ifdef DEBUG
        if (debug)
                printf("crypto_setup: flags 0x%x signature scheme %s\n",
-                   crypto_flags, OBJ_nid2ln(EVP_MD_pkey_type(digest)));
+                   crypto_flags, OBJ_nid2ln(cinfo->nid));
 #endif
 }
 
 
 /*
- * crypto_config - configure crypto data from crypto configuration
- * command.
+ * crypto_config - configure data from crypto configuration command.
  */
 void
 crypto_config(
@@ -1886,8 +2399,17 @@ crypto_config(
                keysdir = emalloc(strlen(cp) + 1);
                strcpy(keysdir, cp);
                break;
+
+       /*
+        * Set certificate trust level. Right now all we do is set the
+        * trust bit; in future things might get much more
+        * sophisticated.
+        */
+       case CRYPTO_CONF_TRST:
+               crypto_flags |= CRYPTO_FLAG_TRUST;
+               break;
        }
-       crypto_flags = CRYPTO_FLAG_ENAB;
+       crypto_flags |= CRYPTO_FLAG_ENAB;
 }
 # else
 int ntp_crypto_bs_pubkey;
index 0d98266d553cb4150a77ebccf9f0bef2661e39be..c91dc397ca559ef63a73647d616ad2fde13a2ad9 100644 (file)
@@ -764,7 +764,7 @@ expire_all(void)
                }
        }
        sys_private = (u_int32)RANDOM & 0xffffffff;
-       crypto_sign();
+       crypto_update();
 }
 #endif /* OPENSSL */
 
index 2e3b2700c2c55d8fe02f5855050f93e975f975f6..650398811c7f8a3b03a8a8b2ec34dd8fa9f7fd3b 100644 (file)
@@ -295,7 +295,6 @@ receive(
 #ifdef OPENSSL
        keyid_t pkeyid, tkeyid;         /* cryptographic keys */
        l_fp    p_org;                  /* originate timestamp */
-       int     is_org;                 /* loopback ok */
        struct autokey *ap;             /* autokey structure pointer */
        struct peer *peer2;             /* aux peer structure pointer */
 #endif /* OPENSSL */
@@ -702,7 +701,7 @@ receive(
                        return;
 #ifdef OPENSSL
                if (crypto_flags && (peer->flags & FLAG_SKEY)) {
-                       crypto_recv(peer, rbufp, 1);
+                       crypto_recv(peer, rbufp);
                        if (peer->flash)
                                unpeer(peer);
                }
@@ -794,11 +793,10 @@ receive(
         * is very likely a valid response to one sent earlier. See
         * below for more jitterbug in other error cases.
         */
-#ifdef OPENSSL
        NTOHL_FP(&pkt->org, &p_org);
-       is_org = L_ISEQU(&peer->xmt, &p_org);
-#endif /* OPENSSL */
-       if (peer->flash) {
+       if (hismode != MODE_BROADCAST && !L_ISEQU(&peer->xmt, &p_org))
+               peer->flash |= TEST2;
+       if (peer->flash & ~TEST2) {
 #ifdef OPENSSL
                switch (hismode) {
 
@@ -809,8 +807,8 @@ receive(
                 * start over.
                 */
                case MODE_SERVER:
-                       if (is_org && has_mac == 4 && pkt->exten[0] ==
-                           0) {
+                       if (!(peer->flash & TEST2) && has_mac == 4 &&
+                           pkt->exten[0] == 0) {
                                if (!(peer->flags & FLAG_CONFIG)) {
                                        unpeer(peer);
                                        return;
@@ -827,7 +825,7 @@ receive(
                 */
                case MODE_ACTIVE:
                case MODE_PASSIVE:
-                       if (is_org) {
+                       if (!(peer->flash & TEST2)) {
                                if (!(peer->flags & FLAG_CONFIG)) {
                                        unpeer(peer);
                                        return;
@@ -869,7 +867,7 @@ receive(
         */
        if (crypto_flags && (peer->flags & FLAG_SKEY)) {
                peer->flash |= TEST10;
-               crypto_recv(peer, rbufp, is_org);
+               crypto_recv(peer, rbufp);
                if (peer->cmmd != 0) {
                        peer->ppoll = pkt->ppoll;
                        poll_update(peer, 0);
@@ -910,7 +908,7 @@ receive(
                 * It will die later after leaving the originate and
                 * receive timestamp in the dust for the reply.
                 */
-               if (peer->flash && is_org) {
+               if (peer->flash) {
                        poll_update(peer, peer->minpoll);
                        if (peer->crypto & CRYPTO_FLAG_PROV) {
 #ifdef DEBUG
@@ -1001,12 +999,9 @@ process_packet(
                peer->oldpkt++;
        if (L_ISEQU(&peer->org, &p_xmt))        /* 1 */
                peer->flash |= TEST1;           /* dupe */
-       if (pmode != MODE_BROADCAST) {
-               if (!L_ISEQU(&peer->xmt, &p_org)) /* 2 */
-                       peer->flash |= TEST2;   /* bogus */
-               if (L_ISZERO(&p_rec) || L_ISZERO(&p_org)) /* test 3 */
-                       peer->flash |= TEST3;   /* unsynch */
-       }
+       if (pmode != MODE_BROADCAST && (L_ISZERO(&p_rec) ||
+           L_ISZERO(&p_org)))                  /* 3 */
+               peer->flash |= TEST3;           /* unsynch */
        if (L_ISZERO(&p_xmt))                   /* 3 */
                peer->flash |= TEST3;           /* unsynch */
        peer->org = p_xmt;
@@ -1368,15 +1363,14 @@ peer_clear(
 #endif
 #ifdef OPENSSL
        key_expire(peer);
-       if (peer->keystr != NULL)
-               free(peer->keystr);
+       if (peer->pkey != NULL)
+               EVP_PKEY_free(peer->pkey);
+       if (peer->subject != NULL)
+               free(peer->subject);
+       if (peer->issuer != NULL)
+               free(peer->issuer);
        if (peer->cmmd != NULL)
                free(peer->cmmd);
-       if (peer->cinfo.ptr != NULL) {
-               cert_free((struct cert_info *)peer->cinfo.ptr);
-               peer->cinfo.ptr = NULL;
-       }
-       value_free(&peer->cinfo);
        value_free(&peer->cookval);
        value_free(&peer->recval);
        value_free(&peer->tai_leap);
@@ -2170,7 +2164,7 @@ peer_xmit(
         */
 #ifdef OPENSSL
        if (crypto_flags && (peer->flags & FLAG_SKEY)) {
-               u_int32 cmmd;
+               struct exten *exten;    /* extension field */
 
                /*
                 * The Public Key Dance (PKD): Cryptographic credentials
@@ -2238,12 +2232,14 @@ peer_xmit(
                 */
                case MODE_BROADCAST:
                        if (peer->flags & FLAG_ASSOC)
-                               cmmd = CRYPTO_AUTO | CRYPTO_RESP;
+                               exten = crypto_args(CRYPTO_AUTO |
+                                   CRYPTO_RESP, peer->associd, NULL);
                        else
-                               cmmd = CRYPTO_ASSOC | CRYPTO_RESP;
-                       cmmd = htonl(cmmd);
-                       sendlen += crypto_xmit(&xpkt, sendlen, &cmmd, 0,
-                           peer->associd);
+                               exten = crypto_args(CRYPTO_ASSOC |
+                                   CRYPTO_RESP, peer->associd, NULL);
+                       sendlen += crypto_xmit(&xpkt, sendlen, exten,
+                           0);
+                       free(exten);
                        break;
 
                /*
@@ -2257,40 +2253,47 @@ peer_xmit(
                 */
                case MODE_ACTIVE:
                case MODE_PASSIVE:
-                       cmmd = 0;
                        if (peer->cmmd != NULL) {
+                               peer->cmmd->associd =
+                                   htonl(peer->associd);
                                sendlen += crypto_xmit(&xpkt, sendlen,
-                                   peer->cmmd, 0, peer->associd);
+                                   peer->cmmd, 0);
                                free(peer->cmmd);
                                peer->cmmd = NULL;
                        }
+                       exten = NULL;
                        if (!peer->crypto)
-                               cmmd = CRYPTO_ASSOC;
+                               exten = crypto_args(CRYPTO_ASSOC,
+                                   peer->assoc, NULL);
+                       else if (peer->pkey == NULL)
+                               exten = crypto_args(CRYPTO_CERT,
+                                   peer->assoc, peer->subject);
                        else if (!(peer->crypto & CRYPTO_FLAG_PROV))
-                               cmmd = CRYPTO_CERT;
+                               exten = crypto_args(CRYPTO_CERT,
+                                   peer->assoc, peer->issuer);
+                       else if (sys_leap != LEAP_NOTINSYNC &&
+                           !(peer->crypto & CRYPTO_FLAG_VRFY))
+                               exten = crypto_args(CRYPTO_SIGN,
+                                   peer->assoc, sys_hostname);
                        else if (!(peer->crypto & CRYPTO_FLAG_AGREE) &&
                            sys_leap != LEAP_NOTINSYNC)
-                               cmmd = CRYPTO_COOK;
+                               exten = crypto_args(CRYPTO_COOK,
+                                   peer->assoc, NULL);
                        else if (peer->flags & FLAG_ASSOC)
-                               cmmd = CRYPTO_AUTO | CRYPTO_RESP;
+                               exten = crypto_args(CRYPTO_AUTO |
+                                   CRYPTO_RESP, peer->associd, NULL);
                        else if (!(peer->crypto & CRYPTO_FLAG_AUTO))
-                               cmmd = CRYPTO_AUTO;
-                       else if (peer->crypto & CRYPTO_FLAG_TAI &&
-                           !(peer->crypto & CRYPTO_FLAG_LEAP) &&
-                           sys_leap != LEAP_NOTINSYNC)
-                               cmmd = CRYPTO_TAI;
-                       if (cmmd != 0) {
-                               if (cmmd & CRYPTO_RESP) {
-                                       cmmd = htonl(cmmd);
-                                       sendlen += crypto_xmit(&xpkt,
-                                           sendlen, &cmmd, 0,
-                                           peer->associd);
-                               } else {
-                                       cmmd = htonl(cmmd);
-                                       sendlen += crypto_xmit(&xpkt,
-                                           sendlen, &cmmd, 0,
-                                           peer->assoc);
-                               }
+                               exten = crypto_args(CRYPTO_AUTO,
+                                   peer->assoc, NULL);
+                       else if (sys_leap != LEAP_NOTINSYNC &&
+                           peer->crypto & CRYPTO_FLAG_TAI &&
+                           !(peer->crypto & CRYPTO_FLAG_LEAP))
+                               exten = crypto_args(CRYPTO_TAI,
+                                   peer->assoc, NULL);
+                       if (exten != NULL) {
+                               sendlen += crypto_xmit(&xpkt, sendlen,
+                                   exten, 0);
+                               free(exten);
                        }
                        break;
 
@@ -2313,28 +2316,45 @@ peer_xmit(
                 * the parameters but the server does not.
                 */
                case MODE_CLIENT:
-                       cmmd = 0;
                        if (peer->cmmd != NULL) {
+                               peer->cmmd->associd =
+                                   htonl(peer->associd);
                                sendlen += crypto_xmit(&xpkt, sendlen,
-                                   peer->cmmd, 0, peer->associd);
+                                   peer->cmmd, 0);
                                free(peer->cmmd);
                                peer->cmmd = NULL;
                        }
+                       exten = NULL;
                        if (!peer->crypto)
-                               cmmd = CRYPTO_ASSOC;
+                               exten = crypto_args(CRYPTO_ASSOC,
+                                   peer->assoc, NULL);
+                       else if (peer->pkey == NULL)
+                               exten = crypto_args(CRYPTO_CERT,
+                                   peer->assoc, peer->subject);
                        else if (!(peer->crypto & CRYPTO_FLAG_PROV))
-                               cmmd = CRYPTO_CERT;
+                               exten = crypto_args(CRYPTO_CERT,
+                                   peer->assoc, peer->issuer);
+                       else if (sys_leap != LEAP_NOTINSYNC &&
+                           !(peer->crypto & CRYPTO_FLAG_VRFY))
+                               exten = crypto_args(CRYPTO_SIGN,
+                                   peer->assoc, sys_hostname);
                        else if (!(peer->crypto & CRYPTO_FLAG_AGREE))
-                               cmmd = CRYPTO_COOK;
+                               exten = crypto_args(CRYPTO_COOK,
+                                   peer->assoc, NULL);
                        else if (!(peer->crypto & CRYPTO_FLAG_AUTO) &&
                            (peer->cast_flags & MDF_BCLNT))
-                               cmmd = CRYPTO_AUTO;
-                       else if (peer->crypto & CRYPTO_FLAG_TAI &&
+                               exten = crypto_args(CRYPTO_AUTO,
+                                   peer->assoc, NULL);
+                       else if (sys_leap != LEAP_NOTINSYNC &&
+                           peer->crypto & CRYPTO_FLAG_TAI &&
                            !(peer->crypto & CRYPTO_FLAG_LEAP))
-                               cmmd = CRYPTO_TAI;
-                       if ((cmmd = htonl(cmmd)) != 0)
+                               exten = crypto_args(CRYPTO_TAI,
+                                   peer->assoc, NULL);
+                       if (exten != NULL) {
                                sendlen += crypto_xmit(&xpkt, sendlen,
-                                   &cmmd, 0, peer->assoc);
+                                   exten, 0);
+                               free(exten);
+                       }
                        break;
                }
 
@@ -2425,6 +2445,7 @@ fast_xmit(
        l_fp xmt_ts;            /* transmit timestamp */
        l_fp xmt_tx;            /* transmit timestamp after authent */
        int sendlen, authlen;
+       u_int32 temp32;
 
        /*
         * Initialize transmit packet header fields from the receive
@@ -2496,7 +2517,6 @@ fast_xmit(
 #ifdef OPENSSL
        if (xkeyid > NTP_MAXKEY) {
                keyid_t cookie;
-               u_int associd;
 
                /*
                 * The only way to get here is a reply to a legitimate
@@ -2509,14 +2529,14 @@ fast_xmit(
                 */
                cookie = session_key(&rbufp->recv_srcadr,
                    &rbufp->dstadr->sin, 0, sys_private, 0);
-               associd = htonl(rpkt->exten[1]);
                if (rbufp->recv_length >= sendlen + MAX_MAC_LEN + 2 *
                    sizeof(u_int32)) {
                        session_key(&rbufp->dstadr->sin,
                            &rbufp->recv_srcadr, xkeyid, 0, 2);
-                       *rpkt->exten |= htonl(CRYPTO_RESP);
+                       temp32 = CRYPTO_RESP;
+                       rpkt->exten[0] |= htonl(temp32);
                        sendlen += crypto_xmit(&xpkt, sendlen,
-                           rpkt->exten, cookie, associd);
+                           (struct exten *)rpkt->exten, cookie);
                } else {
                        session_key(&rbufp->dstadr->sin,
                            &rbufp->recv_srcadr, xkeyid, cookie, 2);
index fc87d8c98184a17519faebbfc08e7cbe07fa28b4..00a5a336b29122d87b6a85968382a99fdd2e85ac 100644 (file)
@@ -279,7 +279,7 @@ timer(void)
        if (revoke_timer <= current_time) {
                revoke_timer += RANDPOLL(sys_revoke);
                expire_all();
-               sprintf(statstr, "refresh ts %u", ntohl(host.tstamp));
+               sprintf(statstr, "refresh ts %u", ntohl(hostval.tstamp));
                record_crypto_stats(NULL, statstr);
 #ifdef DEBUG
                if (debug)
index bb87d1dba343a2ca183a790a059877a8bdc7a5e0..913fa13cf9adcd3003148d9b298f5a20fbda0b42 100644 (file)
@@ -40,7 +40,7 @@ FILE  *fheader P((u_char *)); /* construct file header */
 #ifdef OPENSSL
 int    x509     P((u_char *, EVP_PKEY *, EVP_MD *)); /* generate req/cert */
 void   cb       P((int, int, void *)); /* callback routine */
-u_long asn2ntp  P((ASN1_TIME *));      /* ASN.1 time format to NTP seconds */
+u_long asn2ntp  P((ASN1_TIME *)); /* ASN.1 time to NTP seconds */
 #endif /* OPENSSL */
 
 /*
@@ -76,9 +76,7 @@ main(
        EVP_PKEY *pkey;         /* public/private keys */
        RSA     *rsa;           /* RSA keys */
        DSA     *dsa_params;    /* DSA parameters */
-       DH      *dh_params;     /* Diffie-Hellman parameters */
        u_char  seed[20];       /* seed for DSA parameters */
-       int     codes;          /* DH check codes */
        char    pathbuf[PATH_MAX];
 #endif /* OPENSSL */
        u_char  md5key[16];
@@ -181,9 +179,8 @@ main(
        RSA_print_fp(stdout, pkey->pkey.rsa, 0);
 */
        /*
-        * Generate the X509v3 certificate request. The digest algorithms
-        * that work with RSA are MD2, MD5, SHA, SHA1, MDC2 and
-        * RIPEMD160.
+        * Generate the X509v3 certificates. The digest algorithms that
+        * work with RSA are MD2, MD5, SHA, SHA1, MDC2 and RIPEMD160.
         */
 #ifdef HAVE_EVP_MD2
        x509("RSA_MD2", pkey, EVP_md2());
@@ -233,56 +230,24 @@ main(
        DSA_print_fp(stdout, pkey->pkey.dsa, 0);
 */
        /*
-        * Generate the X509v3 certificate request. The digest algorithms
-        * that work with DSS (DSA) are DSS and DSS1.
+        * Generate the X509v3 certificates. The digest algorithms that
+        * work with DSS (DSA) are DSS and DSS1.
         */
        x509("DSA_SHA", pkey, EVP_dss());
        x509("DSA_SHA1", pkey, EVP_dss1());
        free(pkey);
-
-       /*
-        * Generate Diffie-Hellman parameters.
-        */
-       printf("Generating DH parameters (%d bits)...\n", MODULUSLEN);
-       dh_params = DH_generate_parameters(PRIMELEN, 2, cb, "DH");
-       printf("\n");
-       if (dh_params == NULL) {
-               printf("DH generate parameters fails\n%s\n",
-                   ERR_error_string(ERR_get_error(), NULL));
-               exit (-1);
-       }
-       DH_generate_key(dh_params);
-       if (!DH_check(dh_params, &codes)) {
-               printf("Invalid DH parameters\n");
-               exit (-1);
-       }
-       pkey = EVP_PKEY_new();
-       EVP_PKEY_assign_DH(pkey, dh_params);
-       str = fheader("DHpar");
-       PEM_write_DHparams(str, dh_params);
-       fclose(str);
-       free(pkey);
 #endif /* OPENSSL */
        return (0);
 }
 
 #ifdef OPENSSL
 /*
- * Generate X509v3 certificate request and X509v3 self-signed certificate.
- * Note: At present, this works only for RSA signatures; DSA signatures
- * come a segfault. Later.
- *
- * The certificate request and certificate are saved as files in the
- * format described in the main program. The request consists of the
- * ASN.1 encoding of the version number, subject name, public key and
- * signature. The version number is 1, although this may change in
- * future. The subject name is the host name running this program and
- * the public key is the RSA or DSA key of the subject as selected by
- * the caller.
+ * Generate X509v3 self-signed certificate.
  *
- * The certificate consists of the ASN.1 encoding of the version number,
- * subject name and public key of the request and in addition the serial
- * number, issuer name and validity interval. For a self-signed
+ * The certificate certificate is saved as a file in the format
+ * described in the main program. The certificate consists of the ASN.1
+ * encoding of the version number, serial number, validity interval,
+ * issuer name, subject name and public key. For a self-signed
  * certificate, the issuer name is the same as the subject name and
  * these items are signed using the subject private key. The validity
  * interval extends from the current time to the same time one year
@@ -296,39 +261,12 @@ x509      (
        EVP_MD *md              /* generic digest algorithm */
        )
 {
-       X509_REQ *req;          /* X509 certificate request */
        X509    *cert;          /* X509 certificate */
        X509_NAME *subj;        /* distinguished (common) name */
        FILE    *str;           /* file handle */
        ASN1_INTEGER *serial;   /* serial number */
        u_char  pathbuf[PATH_MAX];
 
-       /*
-        * Generate, sign and verify X509 certificate request.
-        */
-       printf("%s certificate request for %s key type %d\n", id,
-           hostname, EVP_MD_type(pkey));
-       req = X509_REQ_new();
-       X509_REQ_set_version(req, 2L);
-       subj = X509_REQ_get_subject_name(req);
-       X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC,
-           hostname, strlen(hostname), -1, 0);
-       X509_REQ_set_pubkey(req, pkey);
-       X509_REQ_sign(req, pkey, md);
-       if (!X509_REQ_verify(req, pkey)) {
-               printf("Verify request fails\n%s\n",
-                   ERR_error_string(ERR_get_error(), NULL));
-               return (-1);
-       }
-
-       /*
-        * Write request for offline processing.
-        */
-       sprintf(pathbuf, "%sreq", id);
-       str = fheader(pathbuf);
-       PEM_write_X509_REQ(str, req);
-       fclose(str);
-
        /*
         * Generate X509 self-signed certificate.
         *
@@ -378,7 +316,6 @@ x509        (
         */
        ASN1_INTEGER_free(serial);
        X509_free(cert);
-       X509_REQ_free(req);
        fclose(str);
        return (0);
 }