diff options
Diffstat (limited to 'ntpd/ntp_crypto.c')
-rw-r--r-- | ntpd/ntp_crypto.c | 157 |
1 files changed, 101 insertions, 56 deletions
diff --git a/ntpd/ntp_crypto.c b/ntpd/ntp_crypto.c index e66d5c7..5795ae8 100644 --- a/ntpd/ntp_crypto.c +++ b/ntpd/ntp_crypto.c @@ -139,6 +139,7 @@ static int calcomp(struct calendar *pjd1, struct calendar *pjd2) #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 MAX_VALLEN (65535 - VALUE_LEN) #define YEAR (60 * 60 * 24 * 365) /* seconds in year */ /* @@ -179,8 +180,8 @@ static char *rand_file = NULL; /* random seed file */ */ static int crypto_verify (struct exten *, struct value *, struct peer *); -static int crypto_encrypt (struct exten *, struct value *, - keyid_t *); +static int crypto_encrypt (const u_char *, u_int, keyid_t *, + struct value *); static int crypto_alice (struct peer *, struct value *); static int crypto_alice2 (struct peer *, struct value *); static int crypto_alice3 (struct peer *, struct value *); @@ -423,7 +424,6 @@ crypto_recv( 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 */ @@ -448,11 +448,12 @@ crypto_recv( */ authlen = LEN_PKT_NOMAC; hismode = (int)PKT_MODE((&rbufp->recv_pkt)->li_vn_mode); - while ((has_mac = rbufp->recv_length - authlen) > MAX_MAC_LEN) { + while ((has_mac = rbufp->recv_length - authlen) > (int)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; + // HMS: Why pkt[1] instead of ep->associd ? associd = (associd_t)ntohl(pkt[1]); rval = XEVNT_OK; #ifdef DEBUG @@ -473,9 +474,14 @@ crypto_recv( } if (len >= VALUE_LEN) { - tstamp = ntohl(ep->tstamp); fstamp = ntohl(ep->fstamp); vallen = ntohl(ep->vallen); + /* + * Bug 2761: I hope this isn't too early... + */ + if ( vallen == 0 + || len - VALUE_LEN < vallen) + return XEVNT_LEN; } switch (code) { @@ -526,8 +532,9 @@ crypto_recv( rval = XEVNT_ERR; break; } + INSIST(len >= VALUE_LEN); if (vallen == 0 || vallen > MAXHOSTNAME || - len < VALUE_LEN + vallen) { + len - VALUE_LEN < vallen) { rval = XEVNT_LEN; break; } @@ -820,15 +827,24 @@ crypto_recv( * errors. */ if (vallen == (u_int)EVP_PKEY_size(host_pkey)) { + u_int32 *cookiebuf = malloc( + RSA_size(host_pkey->pkey.rsa)); + if (!cookiebuf) { + rval = XEVNT_CKY; + break; + } + if (RSA_private_decrypt(vallen, (u_char *)ep->pkt, - (u_char *)&temp32, + (u_char *)cookiebuf, host_pkey->pkey.rsa, - RSA_PKCS1_OAEP_PADDING) <= 0) { + RSA_PKCS1_OAEP_PADDING) != 4) { rval = XEVNT_CKY; + free(cookiebuf); break; } else { - cookie = ntohl(temp32); + cookie = ntohl(*cookiebuf); + free(cookiebuf); } } else { rval = XEVNT_CKY; @@ -1185,8 +1201,9 @@ crypto_xmit( * choice. */ case CRYPTO_CERT | CRYPTO_RESP: - vallen = ntohl(ep->vallen); - if (vallen == 0 || vallen > MAXHOSTNAME) { + vallen = ntohl(ep->vallen); /* Must be <64k */ + if (vallen == 0 || vallen > MAXHOSTNAME || + len - VALUE_LEN < vallen) { rval = XEVNT_LEN; break; } @@ -1336,7 +1353,10 @@ crypto_xmit( * anything goes wrong. */ case CRYPTO_COOK | CRYPTO_RESP: - if ((opcode & 0xffff) < VALUE_LEN) { + vallen = ntohl(ep->vallen); /* Must be <64k */ + if ( vallen == 0 + || (vallen >= MAX_VALLEN) + || (opcode & 0x0000ffff) < VALUE_LEN + vallen) { rval = XEVNT_LEN; break; } @@ -1344,8 +1364,8 @@ crypto_xmit( tcookie = cookie; else tcookie = peer->hcookie; - if ((rval = crypto_encrypt(ep, &vtemp, &tcookie)) == - XEVNT_OK) { + if ((rval = crypto_encrypt((const u_char *)ep->pkt, vallen, &tcookie, &vtemp)) + == XEVNT_OK) { len = crypto_send(fp, &vtemp, start); value_free(&vtemp); } @@ -1485,13 +1505,16 @@ crypto_verify( * up to the next word (4 octets). */ vallen = ntohl(ep->vallen); - if (vallen == 0) + if ( vallen == 0 + || vallen > MAX_VALLEN) return (XEVNT_LEN); i = (vallen + 3) / 4; siglen = ntohl(ep->pkt[i++]); - if (len < VALUE_LEN + ((vallen + 3) / 4) * 4 + ((siglen + 3) / - 4) * 4) + if ( siglen > MAX_VALLEN + || len - VALUE_LEN < ((vallen + 3) / 4) * 4 + || len - VALUE_LEN - ((vallen + 3) / 4) * 4 + < ((siglen + 3) / 4) * 4) return (XEVNT_LEN); /* @@ -1549,6 +1572,7 @@ crypto_verify( * proventic bit. What a relief. */ EVP_VerifyInit(&ctx, peer->digest); + /* XXX: the "+ 12" needs to be at least documented... */ EVP_VerifyUpdate(&ctx, (u_char *)&ep->tstamp, vallen + 12); if (EVP_VerifyFinal(&ctx, (u_char *)&ep->pkt[i], siglen, pkey) <= 0) @@ -1561,35 +1585,32 @@ crypto_verify( /* - * crypto_encrypt - construct encrypted cookie and signature from - * extension field and cookie + * crypto_encrypt - construct vp (encrypted cookie and signature) from + * the public key and cookie. * - * Returns + * Returns: * XEVNT_OK success * XEVNT_CKY bad or missing cookie * XEVNT_PUB bad or missing public key */ static int crypto_encrypt( - struct exten *ep, /* extension pointer */ - struct value *vp, /* value pointer */ - keyid_t *cookie /* server cookie */ + const u_char *ptr, /* Public Key */ + u_int vallen, /* Length of Public Key */ + keyid_t *cookie, /* server cookie */ + struct value *vp /* value pointer */ ) { EVP_PKEY *pkey; /* public key */ EVP_MD_CTX ctx; /* signature context */ tstamp_t tstamp; /* NTP timestamp */ u_int32 temp32; - u_int len; - const u_char *ptr; u_char *puch; /* * Extract the public key from the request. */ - len = ntohl(ep->vallen); - ptr = (void *)ep->pkt; - pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, len); + pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ptr, vallen); if (pkey == NULL) { msyslog(LOG_ERR, "crypto_encrypt: %s", ERR_error_string(ERR_get_error(), NULL)); @@ -1603,9 +1624,9 @@ crypto_encrypt( tstamp = crypto_time(); vp->tstamp = htonl(tstamp); vp->fstamp = hostval.tstamp; - len = EVP_PKEY_size(pkey); - vp->vallen = htonl(len); - vp->ptr = emalloc(len); + vallen = EVP_PKEY_size(pkey); + vp->vallen = htonl(vallen); + vp->ptr = emalloc(vallen); puch = vp->ptr; temp32 = htonl(*cookie); if (RSA_public_encrypt(4, (u_char *)&temp32, puch, @@ -1623,8 +1644,8 @@ crypto_encrypt( vp->sig = emalloc(sign_siglen); 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)) + EVP_SignUpdate(&ctx, vp->ptr, vallen); + if (EVP_SignFinal(&ctx, vp->sig, &vallen, sign_pkey)) vp->siglen = htonl(sign_siglen); return (XEVNT_OK); } @@ -1695,6 +1716,9 @@ crypto_ident( * call in the protocol module. * * Returns extension field pointer (no errors) + * + * XXX: opcode and len should really be 32-bit quantities and + * we should make sure that str is not too big. */ struct exten * crypto_args( @@ -1707,23 +1731,30 @@ crypto_args( tstamp_t tstamp; /* NTP timestamp */ struct exten *ep; /* extension field pointer */ u_int len; /* extension field length */ + size_t slen = 0; tstamp = crypto_time(); len = sizeof(struct exten); - if (str != NULL) - len += strlen(str); + if (str != NULL) { + slen = strlen(str); + INSIST(slen < MAX_VALLEN); + len += slen; + } ep = emalloc_zero(len); if (opcode == 0) return (ep); + REQUIRE(0 == (len & ~0x0000ffff)); + REQUIRE(0 == (opcode & ~0xffff0000)); + 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((char *)ep->pkt, str, strlen(str)); + ep->vallen = htonl(slen); + memcpy((char *)ep->pkt, str, slen); } return (ep); } @@ -1736,6 +1767,8 @@ crypto_args( * Note: it is not polite to send a nonempty signature with zero * timestamp or a nonzero timestamp with an empty signature, but those * rules are not enforced here. + * + * XXX This code won't work on a box with 16-bit ints. */ int crypto_send( @@ -1745,14 +1778,15 @@ crypto_send( ) { u_int len, vallen, siglen, opcode; - int i, j; + u_int i, j; /* * Calculate extension field length and check for buffer * overflow. Leave room for the MAC. */ - len = 16; + len = 16; /* XXX Document! */ vallen = ntohl(vp->vallen); + INSIST(vallen <= MAX_VALLEN); len += ((vallen + 3) / 4 + 1) * 4; siglen = ntohl(vp->siglen); len += ((siglen + 3) / 4 + 1) * 4; @@ -1773,7 +1807,7 @@ crypto_send( i = 0; if (vallen > 0 && vp->ptr != NULL) { j = vallen / 4; - if (j * 4 < (int)vallen) + if (j * 4 < vallen) ep->pkt[i + j++] = 0; memcpy(&ep->pkt[i], vp->ptr, vallen); i += j; @@ -1786,13 +1820,14 @@ crypto_send( ep->pkt[i++] = vp->siglen; if (siglen > 0 && vp->sig != NULL) { j = siglen / 4; - if (j * 4 < (int)siglen) + if (j * 4 < siglen) ep->pkt[i + j++] = 0; memcpy(&ep->pkt[i], vp->sig, siglen); i += j; } opcode = ntohl(ep->opcode); ep->opcode = htonl((opcode & 0xffff0000) | len); + ENSURE(len <= MAX_VALLEN); return (len); } @@ -1829,7 +1864,6 @@ crypto_update(void) if (hostval.tstamp == 0) return; - /* * Sign public key and timestamps. The filestamp is derived from * the host key file extension from wherever the file was @@ -1946,7 +1980,7 @@ asn_to_calendar ( struct calendar *pjd /* pointer to result */ ) { - int len; /* length of ASN1_TIME string */ + size_t len; /* length of ASN1_TIME string */ char v[24]; /* writable copy of ASN1_TIME string */ unsigned long temp; /* result from strtoul */ @@ -2157,7 +2191,8 @@ crypto_bob( tstamp_t tstamp; /* NTP timestamp */ BIGNUM *bn, *bk, *r; u_char *ptr; - u_int len; + u_int len; /* extension field length */ + u_int vallen = 0; /* value length */ /* * If the IFF parameters are not valid, something awful @@ -2172,8 +2207,11 @@ crypto_bob( /* * Extract r from the challenge. */ - len = ntohl(ep->vallen); - if ((r = BN_bin2bn((u_char *)ep->pkt, len, NULL)) == NULL) { + vallen = ntohl(ep->vallen); + len = ntohl(ep->opcode) & 0x0000ffff; + if (vallen == 0 || len < VALUE_LEN || len - VALUE_LEN < vallen) + return XEVNT_LEN; + if ((r = BN_bin2bn((u_char *)ep->pkt, vallen, NULL)) == NULL) { msyslog(LOG_ERR, "crypto_bob: %s", ERR_error_string(ERR_get_error(), NULL)); return (XEVNT_ERR); @@ -2185,7 +2223,7 @@ crypto_bob( */ bctx = BN_CTX_new(); bk = BN_new(); bn = BN_new(); sdsa = DSA_SIG_new(); - BN_rand(bk, len * 8, -1, 1); /* k */ + BN_rand(bk, vallen * 8, -1, 1); /* k */ BN_mod_mul(bn, dsa->priv_key, r, dsa->q, bctx); /* b r mod q */ BN_add(bn, bn, bk); BN_mod(bn, bn, dsa->q, bctx); /* k + b r mod q */ @@ -2204,30 +2242,37 @@ crypto_bob( * Encode the values in ASN.1 and sign. The filestamp is from * the local file. */ - len = i2d_DSA_SIG(sdsa, NULL); - if (len == 0) { + vallen = i2d_DSA_SIG(sdsa, NULL); + if (vallen == 0) { msyslog(LOG_ERR, "crypto_bob: %s", ERR_error_string(ERR_get_error(), NULL)); DSA_SIG_free(sdsa); return (XEVNT_ERR); } + if (vallen > MAX_VALLEN) { + msyslog(LOG_ERR, "crypto_bob: signature is too big: %d", + vallen); + DSA_SIG_free(sdsa); + return (XEVNT_LEN); + } memset(vp, 0, sizeof(struct value)); tstamp = crypto_time(); vp->tstamp = htonl(tstamp); vp->fstamp = htonl(iffkey_info->fstamp); - vp->vallen = htonl(len); - ptr = emalloc(len); + vp->vallen = htonl(vallen); + ptr = emalloc(vallen); vp->ptr = ptr; i2d_DSA_SIG(sdsa, &ptr); DSA_SIG_free(sdsa); if (tstamp == 0) return (XEVNT_OK); + /* XXX: more validation to make sure the sign fits... */ vp->sig = emalloc(sign_siglen); 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)) + EVP_SignUpdate(&ctx, vp->ptr, vallen); + if (EVP_SignFinal(&ctx, vp->sig, &vallen, sign_pkey)) vp->siglen = htonl(sign_siglen); return (XEVNT_OK); } @@ -3048,7 +3093,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, - hostval.ptr, strlen(hostval.ptr), -1, 0); + hostval.ptr, strlen((const char *)hostval.ptr), -1, 0); subj = X509_get_subject_name(req); X509_set_subject_name(cert, subj); X509_set_pubkey(cert, pkey); @@ -3089,7 +3134,7 @@ cert_sign( vp->vallen = htonl(len); vp->ptr = emalloc(len); ptr = vp->ptr; - i2d_X509(cert, &ptr); + i2d_X509(cert, (unsigned char **)(intptr_t)&ptr); vp->siglen = 0; if (tstamp != 0) { vp->sig = emalloc(sign_siglen); @@ -3421,7 +3466,7 @@ cert_parse( /* * Check for a certificate loop. */ - if (strcmp(hostval.ptr, ret->issuer) == 0) { + if (strcmp((const char *)hostval.ptr, ret->issuer) == 0) { msyslog(LOG_NOTICE, "cert_parse: certificate trail loop %s", ret->subject); |