📄 ntp_crypto.c
字号:
/* * ntp_crypto.c - NTP version 4 public key routines */#ifdef HAVE_CONFIG_H#include <config.h>#endif#ifdef OPENSSL#include <stdio.h>#include <sys/types.h>#include <sys/param.h>#include <unistd.h>#include <fcntl.h>#include "ntpd.h"#include "ntp_stdlib.h"#include "ntp_unixtime.h"#include "ntp_string.h"#include <ntp_random.h>#include "openssl/asn1_mac.h"#include "openssl/bn.h"#include "openssl/err.h"#include "openssl/evp.h"#include "openssl/pem.h"#include "openssl/rand.h"#include "openssl/x509v3.h"#ifdef KERNEL_PLL#include "ntp_syscall.h"#endif /* KERNEL_PLL *//* * Extension field message format * * These are always signed and saved before sending in network byte * order. They must be converted to and from host byte order for * processing. * * +-------+-------+ * | op | len | <- extension pointer * +-------+-------+ * | assocID | * +---------------+ * | timestamp | <- value pointer * +---------------+ * | filestamp | * +---------------+ * | value len | * +---------------+ * | | * = value = * | | * +---------------+ * | signature len | * +---------------+ * | | * = signature = * | | * +---------------+ * * The CRYPTO_RESP bit is set to 0 for requests, 1 for responses. * Requests carry the association ID of the receiver; responses carry * the association ID of the sender. Some messages include only the * operation/length and association ID words and so have length 8 * octets. Ohers include the value structure and associated value and * signature fields. These messages include the timestamp, filestamp, * value and signature words and so have length at least 24 octets. The * signature and/or value fields can be empty, in which case the * respective length words are zero. An empty value with nonempty * signature is syntactically valid, but semantically questionable. * * The filestamp represents the time when a cryptographic data file such * as a public/private key pair is created. It follows every reference * depending on that file and serves as a means to obsolete earlier data * of the same type. The timestamp represents the time when the * cryptographic data of the message were last signed. Creation of a * cryptographic data file or signing a message can occur only when the * creator or signor is synchronized to an authoritative source and * proventicated to a trusted authority. * * Note there are four conditions required for server trust. First, the * public key on the certificate must be verified, which involves a * number of format, content and consistency checks. Next, the server * identity must be confirmed by one of four schemes: private * certificate, IFF scheme, GQ scheme or certificate trail hike to a * self signed trusted certificate. Finally, the server signature must * be verified. *//* * Cryptodefines */#define TAI_1972 10 /* initial TAI offset (s) */#define MAX_LEAP 100 /* max UTC leapseconds (s) */#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 = 0x0; /* status word *//* * Global cryptodata in network byte order */struct cert_info *cinfo = NULL; /* certificate info/value */struct value hostval; /* host value */struct value pubkey; /* public key */struct value tai_leap; /* leapseconds table */EVP_PKEY *iffpar_pkey = NULL; /* IFF parameters */EVP_PKEY *gqpar_pkey = NULL; /* GQ parameters */EVP_PKEY *mvpar_pkey = NULL; /* MV parameters */char *iffpar_file = NULL; /* IFF parameters file */char *gqpar_file = NULL; /* GQ parameters file */char *mvpar_file = NULL; /* MV parameters file *//* * Private cryptodata in host byte order */static char *passwd = NULL; /* private key password */static EVP_PKEY *host_pkey = NULL; /* host key */static EVP_PKEY *sign_pkey = NULL; /* sign key */static const EVP_MD *sign_digest = NULL; /* sign digest */static u_int sign_siglen; /* sign key length */static char *rand_file = NULL; /* random seed file */static char *host_file = NULL; /* host key file */static char *sign_file = NULL; /* sign key file */static char *cert_file = NULL; /* certificate file */static char *leap_file = NULL; /* leapseconds file */static tstamp_t if_fstamp = 0; /* IFF filestamp */static tstamp_t gq_fstamp = 0; /* GQ file stamp */static tstamp_t mv_fstamp = 0; /* MV filestamp */static u_int ident_scheme = 0; /* server identity scheme *//* * Cryptotypes */static int crypto_verify P((struct exten *, struct value *, struct peer *));static int crypto_encrypt P((struct exten *, struct value *, keyid_t *));static int crypto_alice P((struct peer *, struct value *));static int crypto_alice2 P((struct peer *, struct value *));static int crypto_alice3 P((struct peer *, struct value *));static int crypto_bob P((struct exten *, struct value *));static int crypto_bob2 P((struct exten *, struct value *));static int crypto_bob3 P((struct exten *, struct value *));static int crypto_iff P((struct exten *, struct peer *));static int crypto_gq P((struct exten *, struct peer *));static int crypto_mv P((struct exten *, struct peer *));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 int bighash P((BIGNUM *, BIGNUM *));static struct cert_info *crypto_cert P((char *));static void crypto_tai P((char *));#ifdef SYS_WINNTintreadlink(char * link, char * file, int len) { return (-1);}#endif/* * session_key - generate session key * * This routine generates a session key from the source address, * destination address, key ID and private value. The value of the * session key is the MD5 hash of these values, while the next key ID is * the first four octets of the hash. * * Returns the next key ID */keyid_tsession_key( struct sockaddr_storage *srcadr, /* source address */ struct sockaddr_storage *dstadr, /* destination address */ keyid_t keyno, /* key ID */ keyid_t private, /* private value */ u_long lifetime /* key lifetime */ ){ EVP_MD_CTX ctx; /* message digest context */ u_char dgst[EVP_MAX_MD_SIZE]; /* message digest */ keyid_t keyid; /* key identifer */ u_int32 header[10]; /* data in network byte order */ u_int hdlen, len; /* * Generate the session key and key ID. If the lifetime is * greater than zero, install the key and call it trusted. */ hdlen = 0; switch(srcadr->ss_family) { case AF_INET: header[0] = ((struct sockaddr_in *)srcadr)->sin_addr.s_addr; header[1] = ((struct sockaddr_in *)dstadr)->sin_addr.s_addr; header[2] = htonl(keyno); header[3] = htonl(private); hdlen = 4 * sizeof(u_int32); break; case AF_INET6: memcpy(&header[0], &GET_INADDR6(*srcadr), sizeof(struct in6_addr)); memcpy(&header[4], &GET_INADDR6(*dstadr), sizeof(struct in6_addr)); header[8] = htonl(keyno); header[9] = htonl(private); hdlen = 10 * sizeof(u_int32); break; } EVP_DigestInit(&ctx, EVP_md5()); EVP_DigestUpdate(&ctx, (u_char *)header, hdlen); EVP_DigestFinal(&ctx, dgst, &len); memcpy(&keyid, dgst, 4); keyid = ntohl(keyid); if (lifetime != 0) { MD5auth_setkey(keyno, dgst, len); authtrust(keyno, lifetime); }#ifdef DEBUG if (debug > 1) printf( "session_key: %s > %s %08x %08x hash %08x life %lu\n", stoa(srcadr), stoa(dstadr), keyno, private, keyid, lifetime);#endif return (keyid);}/* * make_keylist - generate key list * * Returns * XEVNT_OK success * XEVNT_PER host certificate expired * * This routine constructs a pseudo-random sequence by repeatedly * hashing the session key starting from a given source address, * destination address, private value and the next key ID of the * preceeding session key. The last entry on the list is saved along * with its sequence number and public signature. */intmake_keylist( struct peer *peer, /* peer structure pointer */ struct interface *dstadr /* interface */ ){ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ struct autokey *ap; /* autokey pointer */ struct value *vp; /* value pointer */ keyid_t keyid = 0; /* next key ID */ keyid_t cookie; /* private value */ u_long lifetime; u_int len, mpoll; int i; /* * Allocate the key list if necessary. */ tstamp = crypto_time(); if (peer->keylist == NULL) peer->keylist = emalloc(sizeof(keyid_t) * NTP_MAXSESSION); /* * Generate an initial key ID which is unique and greater than * NTP_MAXKEY. */ while (1) { keyid = (ntp_random() + NTP_MAXKEY + 1) & ((1 << sizeof(keyid_t)) - 1); if (authhavekey(keyid)) continue; break; } /* * Generate up to NTP_MAXSESSION session keys. Stop if the * next one would not be unique or not a session key ID or if * it would expire before the next poll. The private value * included in the hash is zero if broadcast mode, the peer * cookie if client mode or the host cookie if symmetric modes. */ mpoll = 1 << min(peer->ppoll, peer->hpoll); lifetime = min(sys_automax, NTP_MAXSESSION * mpoll); if (peer->hmode == MODE_BROADCAST) cookie = 0; else cookie = peer->pcookie; for (i = 0; i < NTP_MAXSESSION; i++) { peer->keylist[i] = keyid; peer->keynumber = i; keyid = session_key(&dstadr->sin, &peer->srcadr, keyid, cookie, lifetime); lifetime -= mpoll; if (auth_havekey(keyid) || keyid <= NTP_MAXKEY || lifetime <= mpoll) break; } /* * Save the last session key ID, sequence number and timestamp, * then sign these values for later retrieval by the clients. Be * careful not to use invalid key media. Use the public values * timestamp as filestamp. */ vp = &peer->sndval; if (vp->ptr == NULL) vp->ptr = emalloc(sizeof(struct autokey)); ap = (struct autokey *)vp->ptr; ap->seq = htonl(peer->keynumber); ap->key = htonl(keyid); vp->tstamp = htonl(tstamp); vp->fstamp = hostval.tstamp; vp->vallen = htonl(sizeof(struct autokey)); vp->siglen = 0; if (tstamp != 0) { if (tstamp < cinfo->first || tstamp > cinfo->last) return (XEVNT_PER); if (vp->sig == NULL) vp->sig = emalloc(sign_siglen); 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); else msyslog(LOG_ERR, "make_keys %s\n", ERR_error_string(ERR_get_error(), NULL)); peer->flags |= FLAG_ASSOC; }#ifdef DEBUG if (debug) printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n", ntohl(ap->seq), ntohl(ap->key), cookie, ntohl(vp->tstamp), ntohl(vp->fstamp), peer->hpoll);#endif return (XEVNT_OK);}/* * crypto_recv - parse extension fields * * This routine is called when the packet has been matched to an * association and passed sanity, format and MAC checks. We believe the * extension field values only if the field has proper format and * length, the timestamp and filestamp are valid and the signature has * valid length and is verified. There are a few cases where some values * are believed even if the signature fails, but only if the proventic * bit is not set. */intcrypto_recv( struct peer *peer, /* peer structure pointer */ struct recvbuf *rbufp /* packet buffer pointer */ ){ const EVP_MD *dp; /* message digest algorithm */ u_int32 *pkt; /* receive packet pointer */ struct autokey *ap, *bp; /* autokey 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 */ tstamp_t tstamp = 0; /* timestamp */ tstamp_t fstamp = 0; /* filestamp */ u_int len; /* extension field length */ u_int code; /* extension field opcode */ u_int vallen = 0; /* value length */ X509 *cert; /* X509 certificate */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ keyid_t cookie; /* crumbles */ int hismode; /* packet mode */ int rval = XEVNT_OK; u_char *ptr; u_int32 temp32; /* * Initialize. Note that the packet has already been checked for * valid format and extension field lengths. First extract the * field length, command code and association ID in host byte * order. These are used with all commands and modes. Then check * the version number, which must be 2, and length, which must * be at least 8 for requests and VALUE_LEN (24) for responses. * Packets that fail either test sink without a trace. The * association ID is saved only if nonzero. */ authlen = LEN_PKT_NOMAC; hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode); while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) { pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4; ep = (struct exten *)pkt; code = ntohl(ep->opcode) & 0xffff0000; len = ntohl(ep->opcode) & 0x0000ffff; associd = (associd_t) ntohl(pkt[1]); rval = XEVNT_OK;#ifdef DEBUG if (debug) printf( "crypto_recv: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", peer->crypto, authlen, len, code >> 16, associd);#endif /* * Check version number and field length. If bad, * quietly ignore the packet. */ if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8) { sys_unknownversion++; code |= CRYPTO_ERROR; } /* * Little vulnerability bandage here. If a perp tosses a * fake association ID over the fence, we better toss it * out. Only the first one counts. */ if (code & CRYPTO_RESP) { if (peer->assoc == 0) peer->assoc = associd; else if (peer->assoc != associd) code |= CRYPTO_ERROR; } if (len >= VALUE_LEN) { tstamp = ntohl(ep->tstamp); fstamp = ntohl(ep->fstamp); vallen = ntohl(ep->vallen); } switch (code) { /* * Install status word, host name, signature scheme and * association ID. In OpenSSL the signature algorithm is * bound to the digest algorithm, so the NID completely * defines the signature scheme. Note the request and * response are identical, but neither is validated by * signature. The request is processed here only in * symmetric modes. The server name field might be * useful to implement access controls in future. */ case CRYPTO_ASSOC: /* * If the machine is running when this message * arrives, the other fellow has reset and so * must we. Otherwise, pass the extension field * to the transmit side. */ if (peer->crypto) { rval = XEVNT_ERR; break; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -