diff options
Diffstat (limited to 'ntpd/ntp_crypto.c')
-rw-r--r-- | ntpd/ntp_crypto.c | 1009 |
1 files changed, 581 insertions, 428 deletions
diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index 3e67703..84adbdd 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -16,6 +16,7 @@ #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" @@ -98,7 +99,6 @@ * Global cryptodata in host byte order */ u_int32 crypto_flags = 0x0; /* status word */ -u_int sys_tai; /* current UTC offset from TAI */ /* * Global cryptodata in network byte order @@ -107,6 +107,12 @@ 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 @@ -114,22 +120,17 @@ struct value tai_leap; /* leapseconds table */ static char *passwd = NULL; /* private key password */ static EVP_PKEY *host_pkey = NULL; /* host key */ static EVP_PKEY *sign_pkey = NULL; /* sign key */ -static EVP_PKEY *iffpar_pkey = NULL; /* IFF parameters */ -static EVP_PKEY *gqpar_pkey = NULL; /* GQ parameters */ -static EVP_PKEY *mvpar_pkey = NULL; /* MV parameters */ 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 *iffpar_file = NULL; /* IFF parameters file */ -static char *gqpar_file = NULL; /* GQ parameters file */ -static char *mvpar_file = NULL; /* MV parameters file */ static char *cert_file = NULL; /* certificate file */ static char *leap_file = NULL; /* leapseconds file */ -static tstamp_t if_fstamp = 0; /* IFF file stamp */ +static tstamp_t if_fstamp = 0; /* IFF filestamp */ static tstamp_t gq_fstamp = 0; /* GQ file stamp */ -static tstamp_t mv_fstamp = 0; /* MV file stamp */ +static tstamp_t mv_fstamp = 0; /* MV filestamp */ +static u_int ident_scheme = 0; /* server identity scheme */ /* * Cryptotypes @@ -192,6 +193,9 @@ session_key( u_int32 header[10]; /* data in network byte order */ u_int hdlen, len; + if (!dstadr) + return 0; + /* * Generate the session key and key ID. If the lifetime is * greater than zero, install the key and call it trusted. @@ -205,6 +209,7 @@ session_key( header[3] = htonl(private); hdlen = 4 * sizeof(u_int32); break; + case AF_INET6: memcpy(&header[0], &GET_INADDR6(*srcadr), sizeof(struct in6_addr)); @@ -238,13 +243,17 @@ session_key( /* * 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. */ -void +int make_keylist( struct peer *peer, /* peer structure pointer */ struct interface *dstadr /* interface */ @@ -257,9 +266,12 @@ make_keylist( keyid_t keyid = 0; /* next key ID */ keyid_t cookie; /* private value */ u_long lifetime; - u_int len; + u_int len, mpoll; int i; + if (!dstadr) + return XEVNT_OK; + /* * Allocate the key list if necessary. */ @@ -273,9 +285,8 @@ make_keylist( * NTP_MAXKEY. */ while (1) { - keyid = (u_long)RANDOM & 0xffffffff; - if (keyid <= NTP_MAXKEY) - continue; + keyid = (ntp_random() + NTP_MAXKEY + 1) & ((1 << + sizeof(keyid_t)) - 1); if (authhavekey(keyid)) continue; break; @@ -288,7 +299,8 @@ make_keylist( * included in the hash is zero if broadcast mode, the peer * cookie if client mode or the host cookie if symmetric modes. */ - lifetime = min(sys_automax, (unsigned long) NTP_MAXSESSION * (1 <<(peer->kpoll))); + mpoll = 1 << min(peer->ppoll, peer->hpoll); + lifetime = min(sys_automax, NTP_MAXSESSION * mpoll); if (peer->hmode == MODE_BROADCAST) cookie = 0; else @@ -298,9 +310,9 @@ make_keylist( peer->keynumber = i; keyid = session_key(&dstadr->sin, &peer->srcadr, keyid, cookie, lifetime); - lifetime -= 1 << peer->kpoll; + lifetime -= mpoll; if (auth_havekey(keyid) || keyid <= NTP_MAXKEY || - lifetime <= (unsigned long)(1 << (peer->kpoll))) + lifetime <= mpoll) break; } @@ -320,7 +332,10 @@ make_keylist( vp->fstamp = hostval.tstamp; vp->vallen = htonl(sizeof(struct autokey)); vp->siglen = 0; - if (vp->tstamp != 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); @@ -337,8 +352,9 @@ make_keylist( 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->kpoll); + ntohl(vp->tstamp), ntohl(vp->fstamp), peer->hpoll); #endif + return (XEVNT_OK); } @@ -374,14 +390,10 @@ crypto_recv( 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; -#ifdef KERNEL_PLL -#if NTP_API > 3 - struct timex ntv; /* kernel interface structure */ -#endif /* NTP_API */ -#endif /* KERNEL_PLL */ /* * Initialize. Note that the packet has already been checked for @@ -394,6 +406,7 @@ crypto_recv( * 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; @@ -404,7 +417,7 @@ crypto_recv( #ifdef DEBUG if (debug) printf( - "crypto_recv: flags 0x%x ext offset %d len %u code %x assocID %d\n", + "crypto_recv: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", peer->crypto, authlen, len, code >> 16, associd); #endif @@ -413,8 +426,7 @@ crypto_recv( * Check version number and field length. If bad, * quietly ignore the packet. */ - if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8 || - (len < VALUE_LEN && (code & CRYPTO_RESP))) { + if (((code >> 24) & 0x3f) != CRYPTO_VN || len < 8) { sys_unknownversion++; code |= CRYPTO_ERROR; } @@ -444,15 +456,21 @@ crypto_recv( * 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 would be + * symmetric modes. The server name field might be * useful to implement access controls in future. */ case CRYPTO_ASSOC: /* - * Pass the extension field to the transmit - * side. + * 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; + } fp = emalloc(len); memcpy(fp, ep, len); temp32 = CRYPTO_RESP; @@ -464,12 +482,13 @@ crypto_recv( /* * Discard the message if it has already been - * stored or the server is not synchronized. + * stored or the message has been amputated. */ - if (peer->crypto || !fstamp) + if (peer->crypto) break; - if (len < VALUE_LEN + vallen) { + if (vallen == 0 || vallen > MAXHOSTNAME || + len < VALUE_LEN + vallen) { rval = XEVNT_LEN; break; } @@ -481,51 +500,68 @@ crypto_recv( * identity are presumed valid, so we skip the * certificate and identity exchanges and move * immediately to the cookie exchange which - * confirms the server signature. If the client - * has IFF or GC or both, the server must have - * the same one or both. Otherwise, the default - * TC scheme is used. + * confirms the server signature. */ +#ifdef DEBUG + if (debug) + printf( + "crypto_recv: ident host 0x%x server 0x%x\n", + crypto_flags, fstamp); +#endif + temp32 = (crypto_flags | ident_scheme) & + fstamp & CRYPTO_FLAG_MASK; if (crypto_flags & CRYPTO_FLAG_PRIV) { - if (!(fstamp & CRYPTO_FLAG_PRIV)) + if (!(fstamp & CRYPTO_FLAG_PRIV)) { rval = XEVNT_KEY; - else + break; + + } else { fstamp |= CRYPTO_FLAG_VALID | - CRYPTO_FLAG_VRFY; - } else if (crypto_flags & CRYPTO_FLAG_MASK && - !(crypto_flags & fstamp & - CRYPTO_FLAG_MASK)) { + CRYPTO_FLAG_VRFY | + CRYPTO_FLAG_SIGN; + } + /* + * In symmetric modes it is an error if either + * peer requests identity and the other peer + * does not support it. + */ + } else if ((hismode == MODE_ACTIVE || hismode == + MODE_PASSIVE) && ((crypto_flags | fstamp) & + CRYPTO_FLAG_MASK) && !temp32) { + rval = XEVNT_KEY; + break; + /* + * It is an error if the client requests + * identity and the server does not support it. + */ + } else if (hismode == MODE_CLIENT && (fstamp & + CRYPTO_FLAG_MASK) && !temp32) { rval = XEVNT_KEY; + break; } /* - * Discard the message if identity error. + * Otherwise, the identity scheme(s) are those + * that both client and server support. */ - if (rval != XEVNT_OK) - break; + fstamp = temp32 | (fstamp & ~CRYPTO_FLAG_MASK); /* - * Discard the message if the host name length - * is unreasonable or the signature digest NID - * is not supported. + * Discard the message if the signature digest + * NID is not supported. */ temp32 = (fstamp >> 16) & 0xffff; dp = (const EVP_MD *)EVP_get_digestbynid(temp32); - if (vallen == 0 || vallen > MAXHOSTNAME) - rval = XEVNT_LEN; - else if (dp == NULL) + if (dp == NULL) { rval = XEVNT_MD; - if (rval != XEVNT_OK) break; + } /* * Save status word, host name and message - * digest/signature type. If PC identity, be - * sure not to sign the certificate. + * digest/signature type. */ - if (crypto_flags & CRYPTO_FLAG_PRIV) - fstamp |= CRYPTO_FLAG_SIGN; peer->crypto = fstamp; peer->digest = dp; peer->subject = emalloc(vallen + 1); @@ -554,12 +590,8 @@ crypto_recv( case CRYPTO_CERT | CRYPTO_RESP: /* - * Discard the message if invalid or identity - * already confirmed. + * Discard the message if invalid. */ - if (peer->crypto & CRYPTO_FLAG_VRFY) - break; - if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; @@ -589,7 +621,7 @@ crypto_recv( strcpy(peer->issuer, cinfo->issuer); /* - * We plug in the public key and group key in + * We plug in the public key and lifetime from * the first certificate received. However, note * that this certificate might not be signed by * the server, so we can't check the @@ -602,7 +634,7 @@ crypto_recv( peer->pkey = X509_get_pubkey(cert); X509_free(cert); } - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; temp32 = cinfo->nid; sprintf(statstr, "cert %s 0x%x %s (%u) fs %u", cinfo->subject, cinfo->flags, @@ -627,12 +659,13 @@ crypto_recv( case CRYPTO_IFF | CRYPTO_RESP: /* - * Discard the message if invalid or identity - * already confirmed. + * Discard the message if invalid or certificate + * trail not trusted. */ - if (peer->crypto & CRYPTO_FLAG_VRFY) + if (!(peer->crypto & CRYPTO_FLAG_VALID)) { + rval = XEVNT_ERR; break; - + } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; @@ -651,7 +684,7 @@ crypto_recv( peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; sprintf(statstr, "iff fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); @@ -674,12 +707,13 @@ crypto_recv( case CRYPTO_GQ | CRYPTO_RESP: /* - * Discard the message if invalid or identity - * already confirmed. + * Discard the message if invalid or certificate + * trail not trusted. */ - if (peer->crypto & CRYPTO_FLAG_VRFY) + if (!(peer->crypto & CRYPTO_FLAG_VALID)) { + rval = XEVNT_ERR; break; - + } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; @@ -698,7 +732,7 @@ crypto_recv( peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; sprintf(statstr, "gq fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); @@ -714,12 +748,13 @@ crypto_recv( case CRYPTO_MV | CRYPTO_RESP: /* - * Discard the message if invalid or identity - * already confirmed. + * Discard the message if invalid or certificate + * trail not trusted. */ - if (peer->crypto & CRYPTO_FLAG_VRFY) + if (!(peer->crypto & CRYPTO_FLAG_VALID)) { + rval = XEVNT_ERR; break; - + } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; @@ -738,7 +773,7 @@ crypto_recv( peer->crypto |= CRYPTO_FLAG_VRFY | CRYPTO_FLAG_PROV; - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; sprintf(statstr, "mv fs %u", ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); @@ -747,47 +782,6 @@ crypto_recv( printf("crypto_recv: %s\n", statstr); #endif 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 invalid or identity - * not confirmed. - */ - if (!(peer->crypto & CRYPTO_FLAG_VRFY)) - break; - - if ((rval = crypto_verify(ep, NULL, peer)) != - XEVNT_OK) - break; - - /* - * Scan the certificate list to delete old - * versions and link the newest version first on - * the list. - */ - if ((rval = cert_install(ep, peer)) != XEVNT_OK) break; - - peer->crypto |= CRYPTO_FLAG_SIGN; - peer->flash &= ~TEST10; - temp32 = cinfo->nid; - sprintf(statstr, "sign %s 0x%x %s (%u) fs %u", - cinfo->issuer, cinfo->flags, - 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; /* * Cookie request in symmetric modes. Roll a random @@ -797,12 +791,13 @@ crypto_recv( case CRYPTO_COOK: /* - * Discard the message if invalid or identity - * not confirmed. + * Discard the message if invalid or certificate + * trail not trusted. */ - if (!(peer->crypto & CRYPTO_FLAG_VRFY)) + if (!(peer->crypto & CRYPTO_FLAG_VALID)) { + rval = XEVNT_ERR; break; - + } if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; @@ -817,7 +812,7 @@ crypto_recv( fp->opcode |= htonl(temp32); peer->cmmd = fp; if (peer->crypto & CRYPTO_FLAG_AGREE) { - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; break; } @@ -832,7 +827,7 @@ crypto_recv( RAND_bytes((u_char *)&peer->pcookie, 4); peer->crypto &= ~CRYPTO_FLAG_AUTO; peer->crypto |= CRYPTO_FLAG_AGREE; - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; sprintf(statstr, "cook %x ts %u fs %u", peer->pcookie, ntohl(ep->tstamp), ntohl(ep->fstamp)); @@ -855,9 +850,10 @@ crypto_recv( * not confirmed or signature not verified with * respect to the cookie values. */ - if (!(peer->crypto & CRYPTO_FLAG_VRFY)) + if (!(peer->crypto & CRYPTO_FLAG_VRFY)) { + rval = XEVNT_ERR; break; - + } if ((rval = crypto_verify(ep, &peer->cookval, peer)) != XEVNT_OK) break; @@ -896,7 +892,7 @@ crypto_recv( else peer->crypto &= ~CRYPTO_FLAG_AUTO; peer->crypto |= CRYPTO_FLAG_AGREE; - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; sprintf(statstr, "cook %x ts %u fs %u", peer->pcookie, ntohl(ep->tstamp), ntohl(ep->fstamp)); @@ -924,9 +920,10 @@ crypto_recv( * not confirmed or signature not verified with * respect to the receive autokey values. */ - if (!(peer->crypto & CRYPTO_FLAG_VRFY)) + if (!(peer->crypto & CRYPTO_FLAG_VRFY)) { + rval = XEVNT_ERR; break; - + } if ((rval = crypto_verify(ep, &peer->recval, peer)) != XEVNT_OK) break; @@ -946,7 +943,7 @@ crypto_recv( bp->key = ntohl(ap->key); peer->pkeyid = bp->key; peer->crypto |= CRYPTO_FLAG_AUTO; - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; sprintf(statstr, "auto seq %d key %x ts %u fs %u", bp->seq, bp->key, ntohl(ep->tstamp), @@ -957,6 +954,49 @@ crypto_recv( printf("crypto_recv: %s\n", statstr); #endif 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 invalid or not + * proventic. + */ + if (!(peer->crypto & CRYPTO_FLAG_PROV)) { + rval = XEVNT_ERR; + break; + } + if ((rval = crypto_verify(ep, NULL, peer)) != + XEVNT_OK) + break; + + /* + * Scan the certificate list to delete old + * versions and link the newest version first on + * the list. + */ + if ((rval = cert_install(ep, peer)) != XEVNT_OK) + break; + + peer->crypto |= CRYPTO_FLAG_SIGN; + peer->flash &= ~TEST8; + temp32 = cinfo->nid; + sprintf(statstr, "sign %s 0x%x %s (%u) fs %u", + cinfo->issuer, cinfo->flags, + 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; /* * Install leapseconds table in symmetric modes. This @@ -970,12 +1010,8 @@ crypto_recv( case CRYPTO_TAI: /* - * Discard the message if invalid or identity - * not confirmed. + * Discard the message if invalid. */ - if (!(peer->crypto & CRYPTO_FLAG_VRFY)) - break; - if ((rval = crypto_verify(ep, NULL, peer)) != XEVNT_OK) break; @@ -991,7 +1027,7 @@ crypto_recv( fp->opcode |= htonl(temp32); peer->cmmd = fp; if (len <= VALUE_LEN) { - peer->flash &= ~TEST10; + peer->flash &= ~TEST8; break; } /* fall through */ @@ -999,22 +1035,18 @@ crypto_recv( case CRYPTO_TAI | CRYPTO_RESP: /* - * Discard the message if invalid or identity - * not confirmed or signature not verified with - * respect to the leapsecond table values. + * If this is a response, discard the message if + * signature not verified with respect to the + * leapsecond table values. */ - if (!(peer->crypto & CRYPTO_FLAG_VRFY)) - break; - - if ((rval = crypto_verify(ep, &peer->tai_leap, - peer)) != XEVNT_OK) - break; + if (peer->cmmd == NULL) { + if ((rval = crypto_verify(ep, + &peer->tai_leap, peer)) != XEVNT_OK) + break; + } /* - * Initialize peer variables, leapseconds - * structure and extension field in network byte - * order. Since a filestamp may have changed, - * recompute the signatures. + * Initialize peer variables with latest update. */ peer->tai_leap.tstamp = ep->tstamp; peer->tai_leap.fstamp = ep->fstamp; @@ -1035,25 +1067,12 @@ crypto_recv( tai_leap.ptr = emalloc(vallen); memcpy(tai_leap.ptr, ep->pkt, vallen); crypto_update(); - sys_tai = vallen / 4 + TAI_1972 - 1; } crypto_flags |= CRYPTO_FLAG_TAI; peer->crypto |= CRYPTO_FLAG_LEAP; - peer->flash &= ~TEST10; -#ifdef KERNEL_PLL -#if NTP_API > 3 - /* - * If the kernel cooperates, initialize the - * current TAI offset. - */ - ntv.modes = MOD_TAI; - ntv.constant = sys_tai; - (void)ntp_adjtime(&ntv); -#endif /* NTP_API */ -#endif /* KERNEL_PLL */ - sprintf(statstr, "leap %u ts %u fs %u", - vallen, ntohl(ep->tstamp), - ntohl(ep->fstamp)); + peer->flash &= ~TEST8; + sprintf(statstr, "leap %u ts %u fs %u", vallen, + ntohl(ep->tstamp), ntohl(ep->fstamp)); record_crypto_stats(&peer->srcadr, statstr); #ifdef DEBUG if (debug) @@ -1068,6 +1087,7 @@ crypto_recv( * valid field length. Remaining checks are below and on * the transmit side. */ + case CRYPTO_CERT: case CRYPTO_IFF: case CRYPTO_GQ: case CRYPTO_MV: @@ -1076,7 +1096,6 @@ crypto_recv( rval = XEVNT_LEN; break; } - /* fall through */ /* @@ -1088,7 +1107,7 @@ crypto_recv( */ default: if (code & (CRYPTO_RESP | CRYPTO_ERROR)) { - rval = XEVNT_LEN; + rval = XEVNT_ERR; } else if ((rval = crypto_verify(ep, NULL, peer)) == XEVNT_OK) { fp = emalloc(len); @@ -1100,24 +1119,27 @@ crypto_recv( } /* - * We log everything except length/format errors and + * We don't log length/format/timestamp errors and * duplicates, which are log clogging vulnerabilities. * The first error found terminates the extension field - * scan and we return the laundry to the caller. + * scan and we return the laundry to the caller. A + * length/format/timestamp error on transmit is + * cheerfully ignored, as the message is not sent. */ - if (rval != XEVNT_OK) { + if (rval > XEVNT_TSP) { sprintf(statstr, "error %x opcode %x ts %u fs %u", rval, code, tstamp, fstamp); - if (rval > XEVNT_TSP) - record_crypto_stats(&peer->srcadr, - statstr); + record_crypto_stats(&peer->srcadr, statstr); report_event(rval, peer); #ifdef DEBUG if (debug) printf("crypto_recv: %s\n", statstr); #endif break; + + } else if (rval > XEVNT_OK && (code & CRYPTO_RESP)) { + rval = XEVNT_OK; } authlen += len; } @@ -1148,9 +1170,10 @@ crypto_xmit( struct peer *peer; /* peer structure pointer */ u_int opcode; /* extension field opcode */ struct exten *fp; /* extension pointers */ - struct cert_info *cp; /* certificate info/value pointer */ + struct cert_info *cp, *xp; /* certificate info/value pointer */ char certname[MAXHOSTNAME + 1]; /* subject name buffer */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ + tstamp_t tstamp; u_int vallen; u_int len; struct value vtemp; @@ -1170,21 +1193,22 @@ crypto_xmit( fp->associd = htonl(associd); len = 8; rval = XEVNT_OK; + tstamp = crypto_time(); switch (opcode & 0xffff0000) { /* * Send association request and response with status word and * host name. Note, this message is not signed and the filestamp - * contains only the status word. We check at this point whether - * the identity schemes are compatible to save tears later on. + * contains only the status word. */ case CRYPTO_ASSOC | CRYPTO_RESP: + len += crypto_send(fp, &hostval); + fp->fstamp = htonl(crypto_flags); + break; + case CRYPTO_ASSOC: len += crypto_send(fp, &hostval); - if (crypto_time() == 0) - fp->fstamp = 0; - else - fp->fstamp = htonl(crypto_flags); + fp->fstamp = htonl(crypto_flags | ident_scheme); break; /* @@ -1196,17 +1220,15 @@ crypto_xmit( vtemp.tstamp = ep->tstamp; vtemp.fstamp = ep->fstamp; vtemp.vallen = ep->vallen; - vtemp.ptr = (unsigned char *)ep->pkt; + vtemp.ptr = (u_char *)ep->pkt; len += crypto_send(fp, &vtemp); break; /* * Send certificate response or sign request. Use the values - * from the certificate. If the request contains no subject - * name, assume the name of this host. This is for backwards - * compatibility. Light the error bit if no certificate with - * the given subject name is found. Of course, private - * certificates are never sent. + * from the certificate cache. If the request contains no + * subject name, assume the name of this host. This is for + * backwards compatibility. Private certificates are never sent. */ case CRYPTO_SIGN: case CRYPTO_CERT | CRYPTO_RESP: @@ -1214,23 +1236,61 @@ crypto_xmit( if (vallen == 8) { strcpy(certname, sys_hostname); } else if (vallen == 0 || vallen > MAXHOSTNAME) { - opcode |= CRYPTO_ERROR; + rval = XEVNT_LEN; break; } else { memcpy(certname, ep->pkt, vallen); certname[vallen] = '\0'; } + + /* + * Find all certificates with matching subject. If a + * self-signed, trusted certificate is found, use that. + * If not, use the first one with matching subject. A + * private certificate is never divulged or signed. + */ + xp = NULL; for (cp = cinfo; cp != NULL; cp = cp->link) { if (cp->flags & CERT_PRIV) continue; + if (strcmp(certname, cp->subject) == 0) { - len += crypto_send(fp, &cp->cert); - break; + if (xp == NULL) + xp = cp; + if (strcmp(certname, cp->issuer) == + 0 && cp->flags & CERT_TRUST) { + xp = cp; + break; + } } } - if (cp == NULL) - opcode |= CRYPTO_ERROR; + + /* + * Be careful who you trust. If not yet synchronized, + * give back an empty response. If certificate not found + * or beyond the lifetime, return an error. This is to + * avoid a bad dude trying to get an expired certificate + * re-signed. Otherwise, send it. + * + * Note the timestamp and filestamp are taken from the + * certificate value structure. For all certificates the + * timestamp is the latest signature update time. For + * host and imported certificates the filestamp is the + * creation epoch. For signed certificates the filestamp + * is the creation epoch of the trusted certificate at + * the base of the certificate trail. In principle, this + * allows strong checking for signature masquerade. + */ + if (tstamp == 0) + break; + + if (xp == NULL) + rval = XEVNT_CRT; + else if (tstamp < xp->first || tstamp > xp->last) + rval = XEVNT_SRV; + else + len += crypto_send(fp, &xp->cert); break; /* @@ -1238,21 +1298,23 @@ crypto_xmit( */ case CRYPTO_IFF: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { - opcode |= CRYPTO_ERROR; + rval = XEVNT_ERR; break; } - if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) + if ((rval = crypto_alice(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); - value_free(&vtemp); + value_free(&vtemp); + } break; /* * Send response in Schnorr (IFF) identity scheme. */ case CRYPTO_IFF | CRYPTO_RESP: - if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) + if ((rval = crypto_bob(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); - value_free(&vtemp); + value_free(&vtemp); + } break; /* @@ -1260,21 +1322,23 @@ crypto_xmit( */ case CRYPTO_GQ: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { - opcode |= CRYPTO_ERROR; + rval = XEVNT_ERR; break; } - if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) + if ((rval = crypto_alice2(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); - value_free(&vtemp); + value_free(&vtemp); + } break; /* * Send response in Guillou-Quisquater (GQ) identity scheme. */ case CRYPTO_GQ | CRYPTO_RESP: - if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) + if ((rval = crypto_bob2(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); - value_free(&vtemp); + value_free(&vtemp); + } break; /* @@ -1282,21 +1346,23 @@ crypto_xmit( */ case CRYPTO_MV: if ((peer = findpeerbyassoc(ep->pkt[0])) == NULL) { - opcode |= CRYPTO_ERROR; + rval = XEVNT_ERR; break; } - if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) + if ((rval = crypto_alice3(peer, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); - value_free(&vtemp); + value_free(&vtemp); + } break; /* * Send response in MV identity scheme. */ case CRYPTO_MV | CRYPTO_RESP: - if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) + if ((rval = crypto_bob3(ep, &vtemp)) == XEVNT_OK) { len += crypto_send(fp, &vtemp); - value_free(&vtemp); + value_free(&vtemp); + } break; /* @@ -1327,14 +1393,14 @@ crypto_xmit( */ case CRYPTO_COOK | CRYPTO_RESP: if ((opcode & 0xffff) < VALUE_LEN) { - opcode |= CRYPTO_ERROR; + rval = XEVNT_LEN; break; } if (PKT_MODE(xpkt->li_vn_mode) == MODE_SERVER) { tcookie = cookie; } else { if ((peer = findpeerbyassoc(associd)) == NULL) { - opcode |= CRYPTO_ERROR; + rval = XEVNT_ERR; break; } tcookie = peer->pcookie; @@ -1354,7 +1420,7 @@ crypto_xmit( */ case CRYPTO_AUTO | CRYPTO_RESP: if ((peer = findpeerbyassoc(associd)) == NULL) { - opcode |= CRYPTO_ERROR; + rval = XEVNT_ERR; break; } peer->flags &= ~FLAG_ASSOC; @@ -1363,8 +1429,8 @@ crypto_xmit( /* * Send leapseconds table and signature. Use the values from the - * tai structure. If no table has been loaded, just send a - * request. + * tai structure. If no table has been loaded, just send an + * empty request. */ case CRYPTO_TAI: case CRYPTO_TAI | CRYPTO_RESP: @@ -1378,22 +1444,24 @@ crypto_xmit( */ default: if (opcode & CRYPTO_RESP) - opcode |= CRYPTO_ERROR; + rval = XEVNT_ERR; } /* - * We ignore length/format errors and duplicates. Other errors - * are reported to the log and deny further service. To really - * persistent rascals we toss back a kiss-of-death grenade. + * In case of error, flame the log. If a request, toss the + * puppy; if a response, return so the sender can flame, too. */ - if (rval > XEVNT_TSP) { + if (rval != XEVNT_OK) { opcode |= CRYPTO_ERROR; sprintf(statstr, "error %x opcode %x", rval, opcode); record_crypto_stats(srcadr_sin, statstr); + report_event(rval, NULL); #ifdef DEBUG if (debug) printf("crypto_xmit: %s\n", statstr); #endif + if (!(opcode & CRYPTO_RESP)) + return (0); } /* @@ -1405,8 +1473,8 @@ crypto_xmit( #ifdef DEBUG if (debug) printf( - "crypto_xmit: ext offset %d len %u code %x assocID %d\n", - start, len, opcode>> 16, associd); + "crypto_xmit: flags 0x%x ext offset %d len %u code 0x%x assocID %d\n", + crypto_flags, start, len, opcode >> 16, associd); #endif return (len); } @@ -1423,6 +1491,7 @@ crypto_xmit( * XEVNT_PUB bad or missing public key * XEVNT_SGL bad signature length * XEVNT_SIG signature not verified + * XEVNT_ERR protocol error */ static int crypto_verify( @@ -1433,16 +1502,15 @@ crypto_verify( { EVP_PKEY *pkey; /* server public key */ EVP_MD_CTX ctx; /* signature context */ - tstamp_t tstamp; /* timestamp */ - tstamp_t fstamp; /* filestamp */ + tstamp_t tstamp, tstamp1 = 0; /* timestamp */ + tstamp_t fstamp, fstamp1 = 0; /* filestamp */ u_int vallen; /* value length */ u_int siglen; /* signature length */ u_int opcode, len; - int rval; int i; /* - * We require valid opcode and field length, timestamp, + * We require valid opcode and field lengths, timestamp, * filestamp, public key, digest, signature length and * signature, where relevant. Note that preliminary length * checks are done in the main loop. @@ -1457,7 +1525,8 @@ crypto_verify( * header, no need for further checking. */ if (opcode & CRYPTO_ERROR) - return (XEVNT_LEN); + return (XEVNT_ERR); + if (opcode & CRYPTO_RESP) { if (len < VALUE_LEN) return (XEVNT_LEN); @@ -1465,91 +1534,93 @@ crypto_verify( if (len < VALUE_LEN) return (XEVNT_OK); } + /* * We have a value header. Check for valid field lengths. The * field length must be long enough to contain the value header, - * value and signature. If a request and a previous request of - * the same type is pending, discard the previous request. If a - * request but no signature, there is no need for further - * checking. + * value and signature. Note both the value and signature fields + * are rounded up to the next word. */ vallen = ntohl(ep->vallen); - if (len < ((VALUE_LEN + vallen + 3) / 4) * 4) - return (XEVNT_LEN); - i = (vallen + 3) / 4; siglen = ntohl(ep->pkt[i++]); - if (len < VALUE_LEN + vallen + siglen) + if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) / + 4) * 4) return (XEVNT_LEN); - if (!(opcode & CRYPTO_RESP)) { - if (peer->cmmd != NULL) { - if ((opcode | CRYPTO_RESP) == - (ntohl(peer->cmmd->opcode) & 0xffff0000)) { - free(peer->cmmd); - peer->cmmd = NULL; - } else { - return (XEVNT_LEN); - } - } - if (siglen == 0) - return (XEVNT_OK); + /* + * Punt if this is a response with no data. Punt if this is a + * request and a previous response is pending. + */ + if (opcode & CRYPTO_RESP) { + if (vallen == 0) + return (XEVNT_LEN); + } else { + if (peer->cmmd != NULL) + return (XEVNT_LEN); } /* - * We have a signature. Check for valid timestamp and filestamp. - * The timestamp must not precede the filestamp. The timestamp - * and filestamp must not precede the corresponding values in - * the value structure. Once the autokey values have been - * installed, the timestamp must always be later than the - * corresponding value in the value structure. Duplicate - * timestamps are illegal once the cookie has been validated. + * Check for valid timestamp and filestamp. If the timestamp is + * zero, the sender is not synchronized and signatures are + * disregarded. If not, the timestamp must not precede the + * filestamp. The timestamp and filestamp must not precede the + * corresponding values in the value structure, if present. Once + * the autokey values have been installed, the timestamp must + * always be later than the corresponding value in the value + * structure. Duplicate timestamps are illegal once the cookie + * has been validated. + */ + tstamp = ntohl(ep->tstamp); + fstamp = ntohl(ep->fstamp); + if (tstamp == 0) + return (XEVNT_OK); + + if (tstamp < fstamp) + return (XEVNT_TSP); + + if (vp != NULL) { + tstamp1 = ntohl(vp->tstamp); + fstamp1 = ntohl(vp->fstamp); + if ((tstamp < tstamp1 || (tstamp == tstamp1 && + (peer->crypto & CRYPTO_FLAG_AUTO)))) + return (XEVNT_TSP); + + if ((tstamp < fstamp1 || fstamp < fstamp1)) + return (XEVNT_FSP); + } + + /* + * Check for valid signature length, public key and digest + * algorithm. */ - rval = XEVNT_OK; if (crypto_flags & peer->crypto & CRYPTO_FLAG_PRIV) pkey = sign_pkey; else pkey = peer->pkey; - tstamp = ntohl(ep->tstamp); - fstamp = ntohl(ep->fstamp); - if (tstamp == 0 || tstamp < fstamp) { - rval = XEVNT_TSP; - } 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; - - /* - * If a public key and digest is present, and if valid key - * length, check for valid signature. Note that the first valid - * signature lights the proventic bit. - */ - } else if (pkey == NULL || peer->digest == NULL) { - /* fall through */ - } else if (siglen != (u_int) EVP_PKEY_size(pkey)) { - rval = XEVNT_SGL; - } 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, - pkey)) { - if (peer->crypto & CRYPTO_FLAG_VRFY) - peer->crypto |= CRYPTO_FLAG_PROV; - } else { - rval = XEVNT_SIG; - } + if (siglen == 0 || pkey == NULL || peer->digest == NULL) + return (XEVNT_OK); + + if (siglen != (u_int)EVP_PKEY_size(pkey)) + return (XEVNT_SGL); + + /* + * Darn, I thought we would never get here. Verify the + * signature. If the identity exchange is verified, light the + * proventic bit. If no client identity scheme is specified, + * avoid doing the sign exchange. + */ + EVP_VerifyInit(&ctx, peer->digest); + EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12); + if (!EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen, pkey)) + return (XEVNT_SIG); + + if (peer->crypto & CRYPTO_FLAG_VRFY) { + peer->crypto |= CRYPTO_FLAG_PROV; + if (!(crypto_flags & CRYPTO_FLAG_MASK)) + peer->crypto |= CRYPTO_FLAG_SIGN; } -#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 - return (rval); + return (XEVNT_OK); } @@ -1561,6 +1632,7 @@ crypto_verify( * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_CKY bad or missing cookie + * XEVNT_PER host certificate expired */ static int crypto_encrypt( @@ -1610,6 +1682,10 @@ crypto_encrypt( vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); + + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); @@ -1642,9 +1718,6 @@ crypto_ident( * been found and the CRYPTO_FLAG_VALID bit is set, so the * certificate issuer is valid. */ - if (peer->crypto & CRYPTO_FLAG_VRFY) - return (0); - if (peer->ident_pkey != NULL) EVP_PKEY_free(peer->ident_pkey); if (peer->crypto & CRYPTO_FLAG_GQ) { @@ -1688,8 +1761,7 @@ crypto_ident( } /* - * No compatible identity scheme is available. Use the default - * TC scheme. + * No compatible identity scheme is available. Life is hard. */ msyslog(LOG_INFO, "crypto_ident: no compatible identity scheme found"); @@ -1724,6 +1796,9 @@ crypto_args( len += strlen(str); ep = emalloc(len); memset(ep, 0, len); + if (opcode == 0) + return (ep); + ep->opcode = htonl(opcode + len); /* @@ -1813,13 +1888,14 @@ void crypto_update(void) { EVP_MD_CTX ctx; /* message digest context */ - struct cert_info *cp, *cpn, **zp; /* certificate info/value */ + struct cert_info *cp, *cpn; /* certificate info/value */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t tstamp; /* NTP timestamp */ u_int len; if ((tstamp = crypto_time()) == 0) return; + hostval.tstamp = htonl(tstamp); /* @@ -1842,29 +1918,21 @@ crypto_update(void) /* * Sign certificates and timestamps. The filestamp is derived * from the certificate file extension from wherever the file - * was generated. At the same time expired certificates are - * expunged. + * was generated. Note we do not throw expired certificates + * away; they may have signed younger ones. */ - zp = &cinfo; for (cp = cinfo; cp != NULL; cp = cpn) { cpn = cp->link; - if (tstamp > cp->last) { - *zp = cpn; - cert_free(cp); - } else { - 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); - zp = &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); } /* @@ -1946,6 +2014,7 @@ asn2ntp ( */ if (asn1time->length > 13) return ((u_long)(~0)); /* We can't use -1 here. It's invalid */ + v = (char *)asn1time->data; tm.tm_year = (v[0] - '0') * 10 + v[1] - '0'; if (tm.tm_year < 50) @@ -1983,6 +2052,9 @@ bighash( EVP_DigestUpdate(&ctx, ptr, len); EVP_DigestFinal(&ctx, dgst, &len); BN_bin2bn(dgst, len, bk); + + /* XXX MEMLEAK? free ptr? */ + return (1); } @@ -2036,7 +2108,7 @@ bighash( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key - * XEVNT_ID bad or missing identity parameters + * XEVNT_ID bad or missing group key */ static int crypto_alice( @@ -2055,6 +2127,7 @@ crypto_alice( */ if (peer->ident_pkey == NULL) return (XEVNT_ID); + if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_alice: defective key"); return (XEVNT_PUB); @@ -2086,6 +2159,10 @@ crypto_alice( vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); + + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); @@ -2101,7 +2178,9 @@ crypto_alice( * * Returns * XEVNT_OK success - * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing group key + * XEVNT_ERR protocol error + * XEVNT_PER host expired certificate */ static int crypto_bob( @@ -2122,9 +2201,9 @@ crypto_bob( * If the IFF parameters are not valid, something awful * happened or we are being tormented. */ - if (!(crypto_flags & CRYPTO_FLAG_IFF)) { + if (iffpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob: scheme unavailable"); - return (XEVNT_PUB); + return (XEVNT_ID); } dsa = iffpar_pkey->pkey.dsa; @@ -2135,7 +2214,7 @@ crypto_bob( if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); - return (XEVNT_PUB); + return (XEVNT_ERR); } /* @@ -2167,7 +2246,7 @@ crypto_bob( msyslog(LOG_ERR, "crypto_bob %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); - return (XEVNT_PUB); + return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); @@ -2177,6 +2256,10 @@ crypto_bob( vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); + + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); @@ -2193,8 +2276,8 @@ crypto_bob( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing group key * XEVNT_FSP bad filestamp - * XEVNT_ID bad or missing identity parameters */ int crypto_iff( @@ -2216,7 +2299,7 @@ crypto_iff( */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_iff: scheme unavailable"); - return (XEVNT_PUB); + return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_iff: invalid filestamp %u", @@ -2229,7 +2312,7 @@ crypto_iff( } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_iff: missing challenge"); - return (XEVNT_PUB); + return (XEVNT_ID); } /* @@ -2241,7 +2324,7 @@ crypto_iff( if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_iff %s\n", ERR_error_string(ERR_get_error(), NULL)); - return (XEVNT_PUB); + return (XEVNT_ERR); } /* @@ -2262,6 +2345,7 @@ crypto_iff( DSA_SIG_free(sdsa); if (temp == 0) return (XEVNT_OK); + else return (XEVNT_ID); } @@ -2325,7 +2409,8 @@ crypto_iff( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key - * XEVNT_ID bad or missing identity parameters + * XEVNT_ID bad or missing group key + * XEVNT_PER host certificate expired */ static int crypto_alice2( @@ -2344,6 +2429,7 @@ crypto_alice2( */ if (peer->ident_pkey == NULL) return (XEVNT_ID); + if ((rsa = peer->ident_pkey->pkey.rsa) == NULL) { msyslog(LOG_INFO, "crypto_alice2: defective key"); return (XEVNT_PUB); @@ -2375,6 +2461,10 @@ crypto_alice2( vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); + + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); @@ -2390,7 +2480,9 @@ crypto_alice2( * * Returns * XEVNT_OK success - * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing group key + * XEVNT_ERR protocol error + * XEVNT_PER host certificate expired */ static int crypto_bob2( @@ -2411,9 +2503,9 @@ crypto_bob2( * If the GQ parameters are not valid, something awful * happened or we are being tormented. */ - if (!(crypto_flags & CRYPTO_FLAG_GQ)) { + if (gqpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob2: scheme unavailable"); - return (XEVNT_PUB); + return (XEVNT_ID); } rsa = gqpar_pkey->pkey.rsa; @@ -2424,7 +2516,7 @@ crypto_bob2( if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob2 %s\n", ERR_error_string(ERR_get_error(), NULL)); - return (XEVNT_PUB); + return (XEVNT_ERR); } /* @@ -2456,7 +2548,7 @@ crypto_bob2( msyslog(LOG_ERR, "crypto_bob2 %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); - return (XEVNT_PUB); + return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); @@ -2466,6 +2558,10 @@ crypto_bob2( vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); + + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); @@ -2482,8 +2578,9 @@ crypto_bob2( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing group keys + * XEVNT_ERR protocol error * XEVNT_FSP bad filestamp - * XEVNT_ID bad or missing identity parameters */ int crypto_gq( @@ -2505,7 +2602,7 @@ crypto_gq( */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_gq: scheme unavailable"); - return (XEVNT_PUB); + return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_gq: invalid filestamp %u", @@ -2518,7 +2615,7 @@ crypto_gq( } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_gq: missing challenge"); - return (XEVNT_PUB); + return (XEVNT_ID); } /* @@ -2531,7 +2628,7 @@ crypto_gq( if ((sdsa = d2i_DSA_SIG(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_gq %s\n", ERR_error_string(ERR_get_error(), NULL)); - return (XEVNT_PUB); + return (XEVNT_ERR); } /* @@ -2553,6 +2650,7 @@ crypto_gq( DSA_SIG_free(sdsa); if (temp == 0) return (XEVNT_OK); + else return (XEVNT_ID); } @@ -2637,7 +2735,8 @@ crypto_gq( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key - * XEVNT_ID bad or missing identity parameters + * XEVNT_ID bad or missing group key + * XEVNT_PER host certificate expired */ static int crypto_alice3( @@ -2656,6 +2755,7 @@ crypto_alice3( */ if (peer->ident_pkey == NULL) return (XEVNT_ID); + if ((dsa = peer->ident_pkey->pkey.dsa) == NULL) { msyslog(LOG_INFO, "crypto_alice3: defective key"); return (XEVNT_PUB); @@ -2687,6 +2787,10 @@ crypto_alice3( vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); + + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); @@ -2702,7 +2806,8 @@ crypto_alice3( * * Returns * XEVNT_OK success - * XEVNT_PUB bad or missing public key + * XEVNT_ERR protocol error + * XEVNT_PER host certificate expired */ static int crypto_bob3( @@ -2723,9 +2828,9 @@ crypto_bob3( * If the MV parameters are not valid, something awful * happened or we are being tormented. */ - if (!(crypto_flags & CRYPTO_FLAG_MV)) { + if (mvpar_pkey == NULL) { msyslog(LOG_INFO, "crypto_bob3: scheme unavailable"); - return (XEVNT_PUB); + return (XEVNT_ID); } dsa = mvpar_pkey->pkey.dsa; @@ -2736,7 +2841,7 @@ crypto_bob3( if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob3 %s\n", ERR_error_string(ERR_get_error(), NULL)); - return (XEVNT_PUB); + return (XEVNT_ERR); } /* @@ -2773,7 +2878,7 @@ crypto_bob3( msyslog(LOG_ERR, "crypto_bob3 %s\n", ERR_error_string(ERR_get_error(), NULL)); DSA_free(sdsa); - return (XEVNT_PUB); + return (XEVNT_ERR); } vp->vallen = htonl(len); ptr = emalloc(len); @@ -2783,6 +2888,10 @@ crypto_bob3( vp->siglen = 0; if (tstamp == 0) return (XEVNT_OK); + + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + vp->sig = emalloc(sign_siglen); EVP_SignInit(&ctx, sign_digest); EVP_SignUpdate(&ctx, (u_char *)&vp->tstamp, 12); @@ -2799,8 +2908,9 @@ crypto_bob3( * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key + * XEVNT_ID bad or missing group key + * XEVNT_ERR protocol error * XEVNT_FSP bad filestamp - * XEVNT_ID bad or missing identity parameters */ int crypto_mv( @@ -2822,7 +2932,7 @@ crypto_mv( */ if (peer->ident_pkey == NULL) { msyslog(LOG_INFO, "crypto_mv: scheme unavailable"); - return (XEVNT_PUB); + return (XEVNT_ID); } if (ntohl(ep->fstamp) != peer->fstamp) { msyslog(LOG_INFO, "crypto_mv: invalid filestamp %u", @@ -2835,7 +2945,7 @@ crypto_mv( } if (peer->iffval == NULL) { msyslog(LOG_INFO, "crypto_mv: missing challenge"); - return (XEVNT_PUB); + return (XEVNT_ID); } /* @@ -2847,7 +2957,7 @@ crypto_mv( if ((sdsa = d2i_DSAparams(NULL, &ptr, len)) == NULL) { msyslog(LOG_ERR, "crypto_mv %s\n", ERR_error_string(ERR_get_error(), NULL)); - return (XEVNT_PUB); + return (XEVNT_ERR); } /* @@ -2870,6 +2980,7 @@ crypto_mv( DSA_free(sdsa); if (temp == 0) return (XEVNT_OK); + else return (XEVNT_ID); } @@ -3026,7 +3137,7 @@ cert_parse( if (strcmp(ret->subject, ret->issuer) == 0) { if (!X509_verify(cert, ret->pkey)) { msyslog(LOG_INFO, - "cert_parse: invalid signature not verified %s", + "cert_parse: signature not verified %s", pathbuf); cert_free(ret); X509_free(cert); @@ -3040,8 +3151,8 @@ cert_parse( */ if (ret->first > ret->last || ret->first < fstamp) { msyslog(LOG_INFO, - "cert_parse: expired %s", - ret->subject); + "cert_parse: invalid certificate %s first %u last %u fstamp %u", + ret->subject, ret->first, ret->last, fstamp); cert_free(ret); X509_free(cert); return (NULL); @@ -3064,21 +3175,38 @@ cert_parse( /* - * cert_sign - sign x509 certificate and update value structure. + * cert_sign - sign x509 certificate equest 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. + * The certificate request includes a copy of the host certificate, + * which includes the version number, subject name and public key of the + * host. The resulting certificate includes these values plus the + * serial number, issuer name and valid interval of the server. The + * valid interval extends from the current time to the same time one + * year hence. This may extend the life of the signed certificate beyond + * that of the signer certificate. + * + * It is convenient to use the NTP seconds of the current time as the + * serial number. In the value structure the timestamp is the current + * time and the filestamp is taken from the extension field. Note this + * routine is called only when the client clock is synchronized to a + * proventic source, so timestamp comparisons are valid. + * + * The host certificate is valid from the time it was generated for a + * period of one year. A signed certificate is valid from the time of + * signature for a period of one year, but only the host certificate (or + * sign certificate if used) is actually used to encrypt and decrypt + * signatures. The signature trail is built from the client via the + * intermediate servers to the trusted server. Each signature on the + * trail must be valid at the time of signature, but it could happen + * that a signer certificate expire before the signed certificate, which + * remains valid until its expiration. * * Returns * XEVNT_OK success * XEVNT_PUB bad or missing public key * XEVNT_CRT bad or missing certificate * XEVNT_VFY certificate not verified + * XEVNT_PER host certificate expired */ static int cert_sign( @@ -3100,11 +3228,16 @@ cert_sign( /* * Decode ASN.1 objects and construct certificate structure. + * Make sure the system clock is synchronized to a proventic + * source. */ tstamp = crypto_time(); if (tstamp == 0) return (XEVNT_TSP); + if (tstamp < cinfo->first || tstamp > cinfo->last) + return (XEVNT_PER); + ptr = (u_char *)ep->pkt; if ((req = d2i_X509(NULL, &ptr, ntohl(ep->vallen))) == NULL) { msyslog(LOG_ERR, "cert_sign %s\n", @@ -3123,7 +3256,7 @@ cert_sign( /* * Generate X509 certificate signed by this server. For this - * prupose the issuer name is the server name. Also copy any + * purpose the issuer name is the server name. Also copy any * extensions that might be present. */ cert = X509_new(); @@ -3135,7 +3268,7 @@ cert_sign( X509_gmtime_adj(X509_get_notAfter(cert), YEAR); subj = X509_get_issuer_name(cert); X509_NAME_add_entry_by_txt(subj, "commonName", MBSTRING_ASC, - (unsigned char *) sys_hostname, strlen(sys_hostname), -1, 0); + (u_char *)sys_hostname, strlen(sys_hostname), -1, 0); subj = X509_get_subject_name(req); X509_set_subject_name(cert, subj); X509_set_pubkey(cert, pkey); @@ -3209,11 +3342,12 @@ cert_valid( if (cinf->flags & CERT_SIGN) return (XEVNT_OK); + ptr = (u_char *)cinf->cert.ptr; cert = d2i_X509(NULL, &ptr, ntohl(cinf->cert.vallen)); - if (!X509_verify(cert, pkey)) + if (cert == NULL || !X509_verify(cert, pkey)) return (XEVNT_VFY); - cinf->flags |= CERT_SIGN; + X509_free(cert); return (XEVNT_OK); } @@ -3230,7 +3364,7 @@ cert_valid( * * Returns * XEVNT_OK success - * XEVNT_PER certificate expired + * XEVNT_FSP bad or missing filestamp * XEVNT_CRT bad or missing certificate */ int @@ -3240,33 +3374,28 @@ cert_install( ) { 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. - * Note this allows a certificate not-before time to be in the - * future, but not a not-after time to be in the past. */ if ((cp = cert_parse((u_char *)ep->pkt, ntohl(ep->vallen), ntohl(ep->fstamp))) == NULL) return (XEVNT_CRT); - tstamp = crypto_time(); - if (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 heap. If another is found with a later filestamp, discard * the new one and leave the building. + * + * Make a note to study this issue again. An earlier certificate + * with a long lifetime might be overtaken by a later + * certificate with a short lifetime, thus invalidating the + * earlier signature. However, we gotta find a way to leak old + * stuff from the cache, so we do it anyway. */ - rval = XEVNT_OK; yp = cp; zp = &cinfo; for (xp = cinfo; xp != NULL; xp = xp->link) { @@ -3278,7 +3407,7 @@ cert_install( cert_free(xp); } else { cert_free(cp); - return (XEVNT_TSP); + return (XEVNT_FSP); } break; } @@ -3288,29 +3417,42 @@ cert_install( cinfo = yp; /* - * Scan the certificate list to see if Y is signed by X. + * Scan the certificate list to see if Y is signed by X. This is + * independent of order. */ for (yp = cinfo; yp != NULL; yp = yp->link) { for (xp = cinfo; xp != NULL; xp = xp->link) { - if (yp->flags & CERT_ERROR) - continue; /* - * If issuer Y matches subject X and signature Y - * is valid using public key X, then Y is valid. + * If the issuer of certificate Y matches the + * subject of certificate X, verify the + * signature of Y using the public key of X. If + * so, X signs Y. */ - if (strcmp(yp->issuer, xp->subject) != 0) + if (strcmp(yp->issuer, xp->subject) != 0 || + xp->flags & CERT_ERROR) continue; if (cert_valid(yp, xp->pkey) != XEVNT_OK) { yp->flags |= CERT_ERROR; continue; } - xp->flags |= CERT_SIGN; + + /* + * The signature Y is valid only if it begins + * during the lifetime of X; however, it is not + * necessarily an error, since some other + * certificate might sign Y. + */ + if (yp->first < xp->first || yp->first > + xp->last) + continue; + + yp->flags |= CERT_SIGN; /* * If X is trusted, then Y is trusted. Note that - * we might stumble over a self signed + * we might stumble over a self-signed * certificate that is not trusted, at least * temporarily. This can happen when a dude * first comes up, but has not synchronized the @@ -3319,10 +3461,10 @@ cert_install( * this might result in a loop that could * persist until timeout. */ - if (!(xp->flags & CERT_TRUST)) + if (!(xp->flags & (CERT_TRUST | CERT_VALID))) continue; - yp->flags |= CERT_TRUST; + yp->flags |= CERT_VALID; /* * If subject Y matches the server subject name, @@ -3359,7 +3501,7 @@ cert_install( * That was awesome. Now update the timestamps and signatures. */ crypto_update(); - return (rval); + return (XEVNT_OK); } @@ -3432,16 +3574,19 @@ crypto_key( if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) { msyslog(LOG_ERR, "crypto_key: no data %s\n", filename); + (void)fclose(str); return (NULL); } if ((ptr = strrchr(ptr, '.')) == NULL) { msyslog(LOG_ERR, "crypto_key: no filestamp %s\n", filename); + (void)fclose(str); return (NULL); } if (sscanf(++ptr, "%u", fstamp) != 1) { msyslog(LOG_ERR, "crypto_key: invalid timestamp %s\n", filename); + (void)fclose(str); return (NULL); } @@ -3523,16 +3668,19 @@ crypto_cert( if ((ptr = fgets(linkname, MAXFILENAME, str)) == NULL) { msyslog(LOG_ERR, "crypto_cert: no data %s\n", filename); + (void)fclose(str); return (NULL); } if ((ptr = strrchr(ptr, '.')) == NULL) { msyslog(LOG_ERR, "crypto_cert: no filestamp %s\n", filename); + (void)fclose(str); return (NULL); } if (sscanf(++ptr, "%u", &fstamp) != 1) { msyslog(LOG_ERR, "crypto_cert: invalid filestamp %s\n", filename); + (void)fclose(str); return (NULL); } @@ -3542,6 +3690,7 @@ crypto_cert( if (!PEM_read(str, &name, &header, &data, &len)) { msyslog(LOG_ERR, "crypto_cert %s\n", ERR_error_string(ERR_get_error(), NULL)); + (void)fclose(str); return (NULL); } free(header); @@ -3550,6 +3699,7 @@ crypto_cert( name); free(name); free(data); + (void)fclose(str); return (NULL); } free(name); @@ -3559,8 +3709,10 @@ crypto_cert( */ ret = cert_parse(data, len, fstamp); free(data); + (void)fclose(str); if (ret == NULL) return (NULL); + if ((ptr = strrchr(linkname, '\n')) != NULL) *ptr = '\0'; sprintf(statstr, "%s 0x%x len %lu", &linkname[2], ret->flags, @@ -3590,20 +3742,16 @@ crypto_tai( { FILE *str; /* file handle */ char buf[NTP_MAXSTRLEN]; /* file line buffer */ - u_int leapsec[MAX_LEAP]; /* NTP time at leaps */ - u_int offset; /* offset at leap (s) */ + u_int32 leapsec[MAX_LEAP]; /* NTP time at leaps */ + int offset; /* offset at leap (s) */ char filename[MAXFILENAME]; /* name of leapseconds file */ char linkname[MAXFILENAME]; /* file link (for filestamp) */ char statstr[NTP_MAXSTRLEN]; /* statistics for filegen */ tstamp_t fstamp; /* filestamp */ u_int len; - char *ptr; - int rval, i; -#ifdef KERNEL_PLL -#if NTP_API > 3 - struct timex ntv; /* kernel interface structure */ -#endif /* NTP_API */ -#endif /* KERNEL_PLL */ + u_int32 *ptr; + char *dp; + int rval, i, j; /* * Open the file and discard comment lines. If the first @@ -3625,12 +3773,12 @@ crypto_tai( rval = readlink(filename, linkname, MAXFILENAME - 1); if (rval > 0) { linkname[rval] = '\0'; - ptr = strrchr(linkname, '.'); + dp = strrchr(linkname, '.'); } else { - ptr = strrchr(filename, '.'); + dp = strrchr(filename, '.'); } - if (ptr != NULL) - sscanf(++ptr, "%u", &fstamp); + if (dp != NULL) + sscanf(++dp, "%u", &fstamp); else fstamp = 0; tai_leap.fstamp = htonl(fstamp); @@ -3647,22 +3795,26 @@ crypto_tai( */ i = 0; while (i < MAX_LEAP) { - ptr = fgets(buf, NTP_MAXSTRLEN - 1, str); - if (ptr == NULL) + dp = fgets(buf, NTP_MAXSTRLEN - 1, str); + if (dp == NULL) break; + if (strlen(buf) < 1) continue; + if (*buf == '#') continue; - if (sscanf(buf, "%u %u", &leapsec[i], &offset) != 2) + + if (sscanf(buf, "%u %d", &leapsec[i], &offset) != 2) continue; - if (i != (int)(offset - TAI_1972)) { + + if (i != offset - TAI_1972) break; - } + i++; } fclose(str); - if (ptr != NULL) { + if (dp != NULL) { msyslog(LOG_INFO, "crypto_tai: leapseconds file %s error %d", cp, rval); @@ -3671,29 +3823,17 @@ crypto_tai( /* * The extension field table entries consists of the NTP seconds - * of leap insertion in reverse order, so that the most recent - * insertion is the first entry in the table. + * of leap insertion in network byte order. */ - len = i * 4; + len = i * sizeof(u_int32); tai_leap.vallen = htonl(len); ptr = emalloc(len); - tai_leap.ptr = (unsigned char *) ptr; - for (; i >= 0; i--) { - *ptr++ = (char) htonl(leapsec[i]); - } + tai_leap.ptr = (u_char *)ptr; + for (j = 0; j < i; j++) + *ptr++ = htonl(leapsec[j]); crypto_flags |= CRYPTO_FLAG_TAI; - sys_tai = len / 4 + TAI_1972 - 1; -#ifdef KERNEL_PLL -#if NTP_API > 3 - ntv.modes = MOD_TAI; - ntv.constant = sys_tai; - if (ntp_adjtime(&ntv) == TIME_ERROR) - msyslog(LOG_INFO, - "crypto_tai: kernel TAI update failed"); -#endif /* NTP_API */ -#endif /* KERNEL_PLL */ - sprintf(statstr, "%s link %d fs %u offset %u", cp, rval, fstamp, - ntohl(tai_leap.vallen) / 4 + TAI_1972 - 1); + sprintf(statstr, "%s fs %u leap %u len %u", cp, fstamp, + leapsec[--j], len); record_crypto_stats(NULL, statstr); #ifdef DEBUG if (debug) @@ -3727,6 +3867,7 @@ crypto_setup(void) */ if (!crypto_flags) return; + gethostname(filename, MAXFILENAME); bytes = strlen(filename) + 1; sys_hostname = emalloc(bytes); @@ -3806,7 +3947,7 @@ crypto_setup(void) exit (-1); } hostval.vallen = htonl(strlen(sys_hostname)); - hostval.ptr = (unsigned char *) sys_hostname; + hostval.ptr = (u_char *)sys_hostname; /* * Construct public key extension field for agreement scheme. @@ -3909,7 +4050,7 @@ crypto_setup(void) * It the certificate is trusted, the subject must be the same * as the issuer, in other words it must be self signed. */ - if (cinfo->flags & CERT_PRIV && strcmp(cinfo->subject, + if (cinfo->flags & CERT_TRUST && strcmp(cinfo->subject, cinfo->issuer) != 0) { if (cert_valid(cinfo, sign_pkey) != XEVNT_OK) { msyslog(LOG_ERR, @@ -4009,6 +4150,18 @@ crypto_config( break; /* + * Set identity scheme. + */ + case CRYPTO_CONF_IDENT: + if (!strcasecmp(cp, "iff")) + ident_scheme |= CRYPTO_FLAG_IFF; + else if (!strcasecmp(cp, "gq")) + ident_scheme |= CRYPTO_FLAG_GQ; + else if (!strcasecmp(cp, "mv")) + ident_scheme |= CRYPTO_FLAG_MV; + break; + + /* * Set certificate file name. */ case CRYPTO_CONF_CERT: |