From: Harlan Stenn Date: Sat, 23 Mar 2002 06:07:49 +0000 (-0500) Subject: Integrate Dave's certificate changes. X-Git-Tag: NTP_4_1_73~171^2^2 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=c8b06a4789c793ae4ccdb80c7adf5022e094060d;p=thirdparty%2Fntp.git Integrate Dave's certificate changes. bk: 3c9c1bb5IplTt5Z_etMFsxy3tbOxvg --- diff --git a/include/ntp.h b/include/ntp.h index 2546763a35..45f1daa431 100644 --- a/include/ntp.h +++ b/include/ntp.h @@ -1,12 +1,14 @@ /* * ntp.h - NTP definitions for the masses */ - #ifndef NTP_H #define NTP_H #include "ntp_types.h" #include +#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 diff --git a/include/ntp_config.h b/include/ntp_config.h index 98a32efee3..eaee048933 100644 --- a/include/ntp_config.h +++ b/include/ntp_config.h @@ -158,4 +158,5 @@ #define CONF_CRYPTO_LEAP 3 #define CONF_CRYPTO_CERT 4 #define CONF_CRYPTO_RAND 5 +#define CONF_CRYPTO_TRUST 6 #endif /* OPENSSL */ diff --git a/include/ntp_control.h b/include/ntp_control.h index 5bd605ef21..9ec84dcdc1 100644 --- a/include/ntp_control.h +++ b/include/ntp_control.h @@ -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 diff --git a/include/ntp_crypto.h b/include/ntp_crypto.h index 6bf792a05f..b919cf39b4 100644 --- a/include/ntp_crypto.h +++ b/include/ntp_crypto.h @@ -19,10 +19,18 @@ * 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 */ @@ -44,19 +53,19 @@ */ #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 */ @@ -67,30 +76,14 @@ #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 */ diff --git a/include/ntpd.h b/include/ntpd.h index 2cbf1cffce..2a4e1b39f7 100644 --- a/include/ntpd.h +++ b/include/ntpd.h @@ -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 */ diff --git a/libntp/statestr.c b/libntp/statestr.c index 3cf7e101a9..2ffe534d72 100644 --- a/libntp/statestr.c +++ b/libntp/statestr.c @@ -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 */ diff --git a/ntpd/ntp_config.c b/ntpd/ntp_config.c index f14c3d1adb..da926c23da 100644 --- a/ntpd/ntp_config.c +++ b/ntpd/ntp_config.c @@ -18,10 +18,6 @@ #include "ntp_config.h" #include "ntp_cmdargs.h" -#ifdef OPENSSL -# include "ntp_crypto.h" -#endif /* OPENSSL */ - #include #include #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; diff --git a/ntpd/ntp_control.c b/ntpd/ntp_control.c index 31cf4a5232..6d1f6ceca7 100644 --- a/ntpd/ntp_control.c +++ b/ntpd/ntp_control.c @@ -18,10 +18,6 @@ #include #include -#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: diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index b9c4acb293..19f965e6e8 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -88,30 +88,30 @@ #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_". + * Load required host key, default "ntpkey_key_". */ 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_". + * Load optional sign key, default "ntpkey_sign_". 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_". + * Load required self-signed certificate, default + * "ntpkey_cert_". */ 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; diff --git a/ntpd/ntp_peer.c b/ntpd/ntp_peer.c index 0d98266d55..c91dc397ca 100644 --- a/ntpd/ntp_peer.c +++ b/ntpd/ntp_peer.c @@ -764,7 +764,7 @@ expire_all(void) } } sys_private = (u_int32)RANDOM & 0xffffffff; - crypto_sign(); + crypto_update(); } #endif /* OPENSSL */ diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c index 2e3b2700c2..650398811c 100644 --- a/ntpd/ntp_proto.c +++ b/ntpd/ntp_proto.c @@ -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); diff --git a/ntpd/ntp_timer.c b/ntpd/ntp_timer.c index fc87d8c981..00a5a336b2 100644 --- a/ntpd/ntp_timer.c +++ b/ntpd/ntp_timer.c @@ -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) diff --git a/util/genkeys.c b/util/genkeys.c index bb87d1dba3..913fa13cf9 100644 --- a/util/genkeys.c +++ b/util/genkeys.c @@ -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); }