From: Harlan Stenn Date: Fri, 24 Aug 2001 02:52:15 +0000 (-0400) Subject: Changes fro Dave Mills: crypto_recv() improvements, etc. X-Git-Tag: NTP_4_1_71~22 X-Git-Url: http://git.ipfire.org/gitweb.cgi?a=commitdiff_plain;h=024370282419b1be6235635df02f10069442de7a;p=thirdparty%2Fntp.git Changes fro Dave Mills: crypto_recv() improvements, etc. bk: 3b85c15f10aj4Ap9VJUaimO2jp2WwQ --- diff --git a/include/ntpd.h b/include/ntpd.h index b33b6ffab5..f2c371f6b5 100644 --- a/include/ntpd.h +++ b/include/ntpd.h @@ -141,7 +141,7 @@ extern void resetmanycast P((void)); /* ntp_crypto.c */ #ifdef OPENSSL -extern void crypto_recv P((struct peer *, struct recvbuf *)); +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 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 *)); diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index 8db6bd75f4..efd28c13ee 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -275,14 +275,16 @@ make_keylist( vp->fstamp = cinfo.fstamp; vp->vallen = htonl(sizeof(struct autokey)); vp->siglen = 0; - if (vp->sig == NULL) - vp->sig = emalloc(sign_siglen); - EVP_SignInit(&ctx, 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 (vp->tstamp != 0) { + if (vp->sig == NULL) + vp->sig = emalloc(sign_siglen); + EVP_SignInit(&ctx, 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 if (debug) printf("make_keys: %d %08x %08x ts %u fs %u poll %d\n", @@ -306,7 +308,8 @@ make_keylist( void crypto_recv( struct peer *peer, /* peer structure pointer */ - struct recvbuf *rbufp /* packet buffer pointer */ + struct recvbuf *rbufp, /* packet buffer pointer */ + int is_org /* timestamps test */ ) { EVP_MD_CTX ctx; /* signature context */ @@ -349,11 +352,9 @@ crypto_recv( authlen = LEN_PKT_NOMAC; while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) { pkt = (u_int32 *)&rbufp->recv_pkt + authlen / 4; - len = ntohl(pkt[0]) & 0x0000ffff; code = ntohl(pkt[0]) & 0xffff0000; + len = ntohl(pkt[0]) & 0x0000ffff; associd = ntohl(pkt[1]); - if (associd != 0) - peer->assoc = associd; #ifdef DEBUG if (debug) printf( @@ -372,6 +373,8 @@ crypto_recv( peer->flash |= TEST12; return; } + if (associd != 0) + peer->assoc = associd; switch (code) { /* @@ -381,9 +384,13 @@ crypto_recv( * 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.. */ case CRYPTO_ASSOC | CRYPTO_RESP: - if (peer->crypto) + if (peer->crypto || !is_org) break; vp = (struct value *)&pkt[2]; fstamp = ntohl(vp->fstamp); @@ -430,10 +437,15 @@ 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) - return; + if (!peer->crypto || peer->crypto & + CRYPTO_FLAG_PROV || !is_org) + break; vp = (struct value *)&pkt[2]; tstamp = ntohl(vp->tstamp); fstamp = ntohl(vp->fstamp); @@ -523,11 +535,16 @@ 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) - return; - if (peer->cmmd != NULL) + if (!(peer->crypto & CRYPTO_FLAG_PROV) || + peer->crypto & CRYPTO_FLAG_AGREE || + !is_org || peer->cmmd != NULL) break; if ((vp = crypto_verify(pkt, &peer->cookval, peer)) == NULL) @@ -571,11 +588,17 @@ crypto_recv( /* * Decrypt and install session cookie in client and - * symmetric modes. + * 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) - return; + if (!(peer->crypto & CRYPTO_FLAG_PROV) || + !is_org) + break; if ((vp = crypto_verify(pkt, &peer->cookval, peer)) == NULL) break; @@ -605,7 +628,10 @@ crypto_recv( key_expire(peer); peer->cookval.tstamp = vp->tstamp; peer->cookval.fstamp = vp->fstamp; - peer->pcookie = cookie; + if (peer->crypto & CRYPTO_FLAG_AGREE) + peer->pcookie ^= cookie; + else + peer->pcookie = cookie; if (peer->hmode == MODE_CLIENT && !(peer->cast_flags & MDF_BCLNT)) peer->crypto |= CRYPTO_FLAG_AUTO; @@ -630,10 +656,14 @@ 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: - if (!peer->crypto) - return; + if (!(peer->crypto & CRYPTO_FLAG_PROV)) + break; if ((vp = crypto_verify(pkt, &peer->recval, peer)) == NULL) break; @@ -673,11 +703,18 @@ 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) - return; - if (peer->cmmd != NULL) + if (!(peer->crypto & CRYPTO_FLAG_PROV) || + peer->crypto & CRYPTO_FLAG_LEAP || + !is_org || peer->cmmd != NULL) break; ptr32 = emalloc(len); memcpy(ptr32, pkt, len); @@ -689,6 +726,9 @@ crypto_recv( /* fall through */ case CRYPTO_TAI | CRYPTO_RESP: + if (!(peer->crypto & CRYPTO_FLAG_PROV) || + !is_org) + break; if ((vp = crypto_verify(pkt, &peer->tai_leap, peer)) == NULL) break; diff --git a/ntpd/ntp_proto.c b/ntpd/ntp_proto.c index 7948f6620c..91c2a6286b 100644 --- a/ntpd/ntp_proto.c +++ b/ntpd/ntp_proto.c @@ -275,11 +275,11 @@ receive( struct recvbuf *rbufp ) { - register struct peer *peer; - register struct pkt *pkt; - int hismode; - int oflags; - int restrict_mask; + register struct peer *peer; /* peer structure pointer */ + register struct pkt *pkt; /* receive packet pointer */ + int hismode; /* packet mode */ + int oflags; /* temp flags */ + int restrict_mask; /* restrict bits */ int has_mac; /* length of MAC field */ int authlen; /* offset of MAC field */ int is_authentic; /* cryptosum ok */ @@ -288,9 +288,9 @@ receive( #ifdef OPENSSL keyid_t pkeyid, tkeyid; /* cryptographic keys */ l_fp p_org; /* originate timestamp */ - int is_org; /* org matches previous xmt */ - struct autokey *ap; - struct peer *peer2; + int is_org; /* loopback ok */ + struct autokey *ap; /* autokey structure pointer */ + struct peer *peer2; /* aux peer structure pointer */ #endif /* OPENSSL */ int retcode = AM_NOMATCH; @@ -692,7 +692,7 @@ receive( return; #ifdef OPENSSL if (crypto_flags && (peer->flags & FLAG_SKEY)) { - crypto_recv(peer, rbufp); + crypto_recv(peer, rbufp, 1); if (peer->flash) unpeer(peer); } @@ -739,7 +739,7 @@ receive( if (!(peer->flags & FLAG_CONFIG) && has_mac) { peer->flags |= FLAG_AUTHENABLE; #ifdef OPENSSL - if (skeyid > NTP_MAXKEY) { + if (skeyid > NTP_MAXKEY) { /* test 5 */ if (crypto_flags) peer->flags |= FLAG_SKEY; else @@ -747,23 +747,26 @@ receive( } #endif /* OPENSSL */ } + if (peer->hmode == MODE_BROADCAST && + (restrict_mask & RES_DONTTRUST)) /* test 4 */ + peer->flash |= TEST4; /* access denied */ + if (peer->flash) { +#ifdef DEBUG + if (debug) + printf("receive: denied %03x\n", + peer->flash); +#endif + return; + } /* * A valid packet must be from an authentic and allowed source. - * All packets must pass the authentication allowed tests. * Autokey authenticated packets must pass additional tests and * public-key authenticated packets must have the credentials * verified. If all tests are passed, the packet is forwarded * for processing. If not, the packet is discarded and the * association demobilized if appropriate. */ - if (peer->hmode == MODE_BROADCAST && - (restrict_mask & RES_DONTTRUST)) /* test 4 */ - peer->flash |= TEST4; /* access denied */ -#ifdef OPENSSL - NTOHL_FP(&pkt->org, &p_org); - is_org = L_ISEQU(&peer->xmt, &p_org); -#endif /* OPENSSL */ if (peer->flags & FLAG_AUTHENABLE) { if (!(peer->flags & FLAG_AUTHENTIC)) /* test 5 */ peer->flash |= TEST5; /* auth failed */ @@ -781,6 +784,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) { #ifdef OPENSSL switch (hismode) { @@ -789,16 +796,13 @@ receive( * In client mode if the packet matches the last one * sent and is a crypto-NAK, the server has either * restarted or refreshed the private value, so we - * retrieve the server cookie and continue where we left + * start over. * off. */ case MODE_SERVER: if (is_org && has_mac == 4 && pkt->exten[0] == - 0) { - key_expire(peer); - peer->crypto &= ~(CRYPTO_FLAG_AUTO | - CRYPTO_FLAG_AGREE); - } + 0) + peer_clear(peer); break; /* @@ -819,7 +823,8 @@ receive( #endif /* OPENSSL */ #ifdef DEBUG if (debug) - printf("receive: bad auth %03x\n", peer->flash); + printf("receive: bad auth %03x\n", + peer->flash); #endif return; } @@ -830,20 +835,24 @@ receive( * * 1. If there is no key or the key is not auto, do nothing. * - * 2. If an extension field contains a verified signature, it is + * 2. If this packet is in response to the one just previously + * sent or from a broadcast server, do the extension fields. + * Otherwise, assume bogosity and bail out. + * + * 3. If an extension field contains a verified signature, it is * self-authenticated and we sit the dance. * - * 3. If this is a server reply, check only to see that the + * 4. If this is a server reply, check only to see that the * transmitted key ID matches the received key ID. * - * 4. Check to see that one or more hashes of the current key ID + * 5. Check to see that one or more hashes of the current key ID * matches the previous key ID or ultimate original key ID * obtained from the broadcaster or symmetric peer. If no * match, sit the dance and wait for timeout. */ if (crypto_flags && (peer->flags & FLAG_SKEY)) { peer->flash |= TEST10; - crypto_recv(peer, rbufp); + crypto_recv(peer, rbufp, is_org); if (peer->flash & TEST12) { /* fall through */ @@ -872,25 +881,28 @@ receive( } /* - * This is delicious. Ordinarily, we kick out all errors - * at this point; however, in symmetric mode and just - * warming up, an unsynchronized peer must inject the - * timestamps, even if it fails further up the road. So, - * let the dude by here, but only if the jerk is not yet - * reachable. After that, he's on his own. + * If errors flash at this point and loopback, clamp the + * poll to minimum. If the certificate has been stored, + * scratch the association. If the server is not + * reachable, assume this is the first symmetric mode + * packet that warmed up the association and let it by. + * It will die later after leaving the originate and + * receive timestamp in the dust for the reply. */ - if (peer->flash && is_org && (peer->crypto & - CRYPTO_FLAG_PROV)) { + if (peer->flash && is_org) { + poll_update(peer, peer->minpoll); + if (peer->crypto & CRYPTO_FLAG_PROV) { + if (peer->flags & FLAG_CONFIG) + peer_clear(peer); + else + unpeer(peer); #ifdef DEBUG - if (debug) - printf("packet: bad crypto %03x\n", - peer->flash); + if (debug) + printf("packet: bad crypto %03x\n", + peer->flash); #endif - if (!(peer->flags & FLAG_CONFIG)) - unpeer(peer); - else - peer_clear(peer); - return; + return; + } } if (!(peer->crypto & CRYPTO_FLAG_PROV)) peer->flash |= TEST11;